Components

Listbox

Listboxes allow a user to select a value or multiple values from a custom-styled dropdown.

Pro tip

Listboxes should only be used when multiselect or custom styles are needed. If you can get by with using the browser styles provided by the {' '} component, you should use it first.

While Listbox is similar to the component, they differ in that listboxes represent a selection by the user. Items in a listbox do not have their own individual actions. You can use the ListboxButton component to easily present a listbox in a popout with a button that matches the appearance of our component.

import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<Box display="flex" alignItems="center">
<Text id="mylabel" mr={300} fontSize={300}>Favorite Fruit</Text>
<ListboxButton
minWidth="150px"
aria-labelledby="mylabel"
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || "Select a fruit"}
</ListboxButton>
</Box>
)
}

Properties

NameTypeDefaultDescriptionRequired?
childrenReact.Node
value?string | string[]Any item(s) matching this value will be rendered in a selected state
onChange(event) => ()Called whenever a new value is selected.
innerRefReact.RefUsed if you need to compose Listbox with Downshift
multiselectbooleanfalseWhether or not the listbox should allow multiple selections
footerContentReact.NodeContent to display below the Listbox.Items. Any interactive elements that you want to display at the bottom of the Listbox should be passed here, not as children.

Accessible labels

There are two ways to make sure a Listbox has an accessible label.

  1. Use aria-labelledby with the same value as an id on either text nearby that serves as a label or you can use VisuallyHidden text. Note that the id of the button component will be appended to the value of aria-labelledby for accessibility. This example will read "Favorite Fruit Select a Fruit".
import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<Box display="flex" alignItems="center">
<Text id="mylabel" mr={300} fontSize={300}>Favorite Fruit</Text>
<ListboxButton
minWidth="150px"
aria-labelledby="mylabel"
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || "Select a fruit"}
</ListboxButton>
</Box>
)
}
  1. Use aria-label with a text value that describes the function of the control. Note that the value of the button will be appended to the value of aria-label for accessibility. This example will also read "Favorite Fruit Select a Fruit".
import { Listbox, ListboxButton, useSelect, Text } from '@sproutsocial/racine'
() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<ListboxButton
minWidth="150px"
aria-label="Favorite Fruit"
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || "Select a fruit"}
</ListboxButton>
)
}

Subcomponents

Listbox group

Groups are used to provide default padding and spacing around items, and can optionally render a visual label for a group of items.

<Listbox.Group title="Fruit">
<Box bg="container.background.base" border={500} borderColor="container.border.base" p={400} />
</Listbox.Group>
NameTypeDefaultDescriptionRequired?
childrenReact.Node
titleReact.RefIf provided, a group header will be rendered in the listbox
appearancedefault, flushdefaultUse the flush appearance if you're using the listbox group with a title outside of a popout
disabledbooleanfalseIf all of the items in a group are disabled, the group title should also be disabled

Listbox item

Represents a unique value within a listbox. Selected items are indicated with a check icon and text.

<Box>
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange" selected>
Orange
</Listbox.Item>
</Box>
NameTypeDefaultDescriptionRequired?
childrenReact.Node
disabledbooleanfalse
valuestring
elemBeforeReact.NodeA component to be rendered before the children of the menu item
elemAfterReact.NodeA component to be rendered after the children of the menu item

Listbox Checkbox

A listbox item with a visible representing a selection.

<Listbox.Checkbox value="Apple">Apple</Listbox.Checkbox>

Listbox Radio

A listbox item with a visible representing a selection.

<Listbox.Radio value="Apple">Apple</Listbox.Radio>

Listbox Switch

A Listbox Item with a visible representing a toggleable option. Use the placement prop to determine whether the Switch is positioned before its children or after the children. It will override the elemBefore or elemAfter prop of Listbox.Item accordingly.

() => {
const [isChecked, setIsChecked] = useState(true);
return (
<Listbox.Switch placement="after" onClick={() => setIsChecked(!isChecked)} selected={isChecked}>
Show Archived Tags
</Listbox.Switch>
);
}

Switches may also be used directly in the footer of the Listbox using when appropriate. Do not use Listbox.Switch in this case.

Listbox.Switch accepts the following props in addition to Listbox.Item's props:

NameTypeDefaultDescriptionRequired?
placementbefore,afterShould the Switch be rendered in the elemBefore or elemAfter position?
switchPropsOmit<TypeSwitchProps, 'onClick'>Any additional props to pass to the Switch. onClick is omitted because the top-level onClick prop should be used instead.
labelPropsOmit<TypeTextProps, 'children'>Any additional props to add to the Switch's label. children is omitted because Listbox.Switch's children is rendered as the label's children.

Listbox filter input

A special version of that can be used to allow the user to filter items within a listbox.

() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<ListboxButton
minWidth="200px"
aria-label="Favorite fruit"
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.FilterInput mb={300} />
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || "Select a fruit"}
</ListboxButton>
)
}

Listbox divider

A horizontal line with the "separator" role. Accepts the same props as

Recipes

Changing the listbox button size

ListboxButton offers a small and large size for matching the size of other elements in a form. The following example shows the small size:

() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<ListboxButton
minWidth="150px"
aria-label="Favorite fruit"
size='small'
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || 'Select a fruit'}
</ListboxButton>
)
}

Multiselect

() => {
const { value, onChange } = useMultiselect()
const currentOption =
value.length > 1 ? value.length + ' selected' : value.length ? value : null
return (
<ListboxButton
minWidth="150px"
aria-label="Favorite fruit"
content={
<Listbox multiselect onChange={onChange} value={value} width="200px">
<Listbox.Group>
<Listbox.Checkbox value="Apple">Apple</Listbox.Checkbox>
<Listbox.Checkbox value="Orange">Orange</Listbox.Checkbox>
<Listbox.Checkbox value="Banana">Banana</Listbox.Checkbox>
</Listbox.Group>
</Listbox>
}
>
{currentOption || "Select a fruit"}
</ListboxButton>
)
}

Groups

() => {
const { value, onChange } = useSelect({ initialValue: null })
return (
<ListboxButton
minWidth="150px"
aria-label="Favorite fruit"
content={
<Listbox onChange={onChange} value={value} width="200px">
<Listbox.Group title="Fruit">
<Listbox.Item value="Apple">Apple</Listbox.Item>
<Listbox.Item value="Orange">Orange</Listbox.Item>
<Listbox.Item value="Banana">Banana</Listbox.Item>
</Listbox.Group>
<Listbox.Group title="More fruit">
<Listbox.Item value="Kiwi">Kiwi</Listbox.Item>
<Listbox.Item value="Papaya">Papaya</Listbox.Item>
<Listbox.Item value="Mango">Mango</Listbox.Item>
</Listbox.Group>
</Listbox>
}
>
{value || "Select a fruit"}
</ListboxButton>
)
}

Using dividers and footerContent

One use for Listbox.Divider is to separate Listbox items from the footer. Any information that you would like to display below the items should be passed to Listbox as . It is especially important to use footerContent when including interactive content, as it will not be accessible to keyboard users if passed as children.

<Box
width="220px"
border={500}
borderRadius="outer"
borderColor="container.border.base"
boxShadow='medium'
>
<Listbox
footerContent={
<Box p={350}>
<Link onClick={() => {}}>
View Fruit Help
</Link>
</Box>
}
>
<Listbox.Group title="Fruit">
<Listbox.Item onClick={() => alert('Apple!')}>Apple</Listbox.Item>
<Listbox.Item onClick={() => alert('Orange!')}>Orange</Listbox.Item>
<Listbox.Item onClick={() => alert('Banana!')}>Banana</Listbox.Item>
</Listbox.Group>
<Listbox.Divider />
</Listbox>
</Box>