Components
Menu (base-ui powered)
Menu is a family of Base UI powered components for action menus, split buttons, selects, comboboxes and searchable selects.
It is built on Base UI, which handles focus management, keyboard navigation and ARIA roles, so every menu below is accessible by default. Seeds supplies the styling and a simpler API on top.
Every example on this page is live: edit the code to try different props.
Using Menu
Menu ships from the base entry point of @sproutsocial/seeds-react-menu. Import its stylesheet once in your app, then import the components you need:
import "@sproutsocial/seeds-react-menu/base/css";import { ActionMenu, MenuItem, MenuButton, SingleSelect, MultiSelect,} from "@sproutsocial/seeds-react-menu/base";
MenuV3 object (for example const { ActionMenu, MenuItem } = MenuV3;) and the sample book data from menuV3Data. In your own code, import them directly as shown above.Choosing a menu
| Component | Use it for |
|---|---|
| ActionMenu | A list of actions opened from a trigger, with optional groups, icons, checkbox/radio items and nested submenus. |
| MenuButton | A split button: a primary action plus a dropdown of related actions. |
| SingleSelect | Choosing one option from a short or medium list. |
| MultiSelect | Choosing several options from a short or medium list. |
| SingleCombobox | Typing to filter a long list and choose one option. |
| MultiCombobox | Typing to filter a long list and choose several options, shown as tokens. |
| SingleSearchableSelect | A select with a search field inside the popup, for one option. |
| MultiSearchableSelect | A select with a search field inside the popup, for several options. |
Working with data
The select and combobox components share a data-driven API:
datais an array of items. Each item needs a uniqueid, and can setdisabled: true.itemToStringturns an item into the text used for its accessible name, for filtering, and for the default display.renderItem(optional) customizes how each option looks in the list.- Selection is tracked by
id. Single components useselectedItemId/defaultSelectedItemId/onSelectedItemIdChange. Multi components useselectedItemIds/defaultSelectedItemIds/onSelectedItemIdsChange. - Labels come from wrapping the component in a
FormFieldand passing itsidthrough, as in every example below.
ActionMenu
ActionMenu opens a list of commands from a trigger. Use it when each item does something (compose, edit, delete) rather than choosing a value. If people are picking a value, use one of the select or combobox components below instead.
Pass any button as the trigger, and compose the list from MenuItem, MenuGroup, MenuRadioGroup, MenuSub and MenuSubTrigger.
Basic
Pass a trigger and a list of MenuItems. Each item's onClick runs when it is chosen, and the menu closes afterward.
Example:
function BasicActionMenuExample() {const { ActionMenu, MenuItem } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<ActionMenu trigger={<Button appearance="secondary">Open Menu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></ActionMenu>);}
With Groups
Wrap related items in MenuGroup to give them a label. Add showSeparator to draw a divider after the group.
Example:
function WithGroupsActionMenuExample() {const { ActionMenu, MenuGroup, MenuItem } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<ActionMenu trigger={<Button appearance="secondary">Open Menu</Button>}><MenuGroup label="Actions" showSeparator><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem></MenuGroup><MenuGroup label="Settings"><MenuItem onClick={action("Settings")}>Settings</MenuItem><MenuItem onClick={action("Help")}>Help</MenuItem></MenuGroup></ActionMenu>);}
With Icons
Item content can be any element. Pair an Icon (with aria-hidden) with text to help people scan the menu. Give an item an href (and optionally target) to render it as a link.
Example:
function WithIconsActionMenuExample() {const { ActionMenu, MenuItem } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<ActionMenu trigger={<Button appearance="secondary">Open Menu</Button>}><MenuItem onClick={action("Compose")}><Box display="flex" alignItems="center" gap="300"><Icon name="pencil-to-square-outline" aria-hidden />Compose</Box></MenuItem><MenuItem onClick={action("View Messages")}><Box display="flex" alignItems="center" gap="300"><Icon name="envelope-outline" aria-hidden />View Messages</Box></MenuItem><MenuItem href="https://sproutsocial.com" target="_blank"><Box display="flex" alignItems="center" gap="300"><Icon name="circle-question-outline" aria-hidden />Visit Help Center<Iconname="arrow-right-up"aria-label="open in a new window"style={{ marginLeft: "auto" }}/></Box></MenuItem></ActionMenu>);}
With Disabled Items
Set disabled on a MenuItem to show an action that isn't available right now. Disabled items stay visible so people know the action exists.
Example:
function WithDisabledItemsActionMenuExample() {const { ActionMenu, MenuItem } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<ActionMenu trigger={<Button appearance="secondary">Open Menu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")} disabled>View Messages (disabled)</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></ActionMenu>);}
Checkbox Items
Set type="checkbox" on a MenuItem to make it a toggle. Control it with checked and onCheckedChange. Checkbox items keep the menu open when clicked, so several can be toggled at once.
Example:
function CheckboxItemsActionMenuExample() {const { ActionMenu, MenuItem } = MenuV3;const [highContrast, setHighContrast] = React.useState(false);const [darkMode, setDarkMode] = React.useState(false);const [notifications, setNotifications] = React.useState(true);return (<ActionMenu trigger={<Button appearance="secondary">Preferences</Button>}><MenuItemtype="checkbox"checked={highContrast}onCheckedChange={setHighContrast}>High contrast</MenuItem><MenuItemtype="checkbox"checked={darkMode}onCheckedChange={setDarkMode}>Dark mode</MenuItem><MenuItemtype="checkbox"checked={notifications}onCheckedChange={setNotifications}>Notifications</MenuItem></ActionMenu>);}
Radio Items
Wrap items with type="radio" in a MenuRadioGroup to let people choose one option from a set. Control the group with value and onValueChange.
Example:
function RadioItemsActionMenuExample() {const { ActionMenu, MenuGroup, MenuItem, MenuRadioGroup } = MenuV3;const [theme, setTheme] = React.useState("system");return (<ActionMenu trigger={<Button appearance="secondary">Theme</Button>}><MenuGroup label="Appearance"><MenuRadioGroup value={theme} onValueChange={setTheme}><MenuItem type="radio" value="light">Light</MenuItem><MenuItem type="radio" value="dark">Dark</MenuItem><MenuItem type="radio" value="system">System default</MenuItem></MenuRadioGroup></MenuGroup></ActionMenu>);}
Controlled Open
Control whether the menu is open with open and onOpenChange. Use this when something else on the page needs to open or close the menu.
Example:
function ControlledOpenActionMenuExample() {const { ActionMenu, MenuItem } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);const [open, setOpen] = React.useState(false);return (<Box><Button appearance="primary" onClick={() => setOpen(true)} mb={300}>Open programmatically</Button><ActionMenutrigger={<Button appearance="secondary">Menu</Button>}open={open}onOpenChange={(isOpen) => {setOpen(isOpen);}}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></ActionMenu></Box>);}
Nested Submenu
Use MenuSub with a MenuSubTrigger to open a second-level menu.
Example:
function NestedSubmenuActionMenuExample() {const { ActionMenu, MenuItem, MenuSub, MenuSubTrigger } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<ActionMenu trigger={<Button appearance="secondary">Open Menu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuSub trigger={<MenuSubTrigger>Share</MenuSubTrigger>}><MenuItem onClick={action("Share to Twitter")}>Twitter</MenuItem><MenuItem onClick={action("Share to LinkedIn")}>LinkedIn</MenuItem><MenuSub trigger={<MenuSubTrigger>More platforms</MenuSubTrigger>}><MenuItem onClick={action("Share to Facebook")}>Facebook</MenuItem><MenuItem onClick={action("Share to Instagram")}>Instagram</MenuItem></MenuSub></MenuSub><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></ActionMenu>);}
Mixed Item Types
Action, checkbox and radio items can be combined in one menu. Use groups and separators to keep each kind of item distinct.
Example:
function MixedItemTypesActionMenuExample() {const { ActionMenu, MenuGroup, MenuItem, MenuRadioGroup } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);const [notifications, setNotifications] = React.useState(true);const [theme, setTheme] = React.useState("system");return (<ActionMenu trigger={<Button appearance="secondary">Settings</Button>}><MenuGroup label="Actions" showSeparator><MenuItem onClick={action("Edit profile")}>Edit profile</MenuItem><MenuItem onClick={action("Manage team")}>Manage team</MenuItem></MenuGroup><MenuGroup label="Preferences" showSeparator><MenuItemtype="checkbox"checked={notifications}onCheckedChange={setNotifications}>Email notifications</MenuItem></MenuGroup><MenuGroup label="Appearance"><MenuRadioGroup value={theme} onValueChange={setTheme}><MenuItem type="radio" value="light">Light</MenuItem><MenuItem type="radio" value="dark">Dark</MenuItem><MenuItem type="radio" value="system">System</MenuItem></MenuRadioGroup></MenuGroup></ActionMenu>);}
MenuButton
MenuButton is a split button: a main action with a chevron next to it that opens related actions. Use it when one action is the most common but people sometimes need an alternative, like "Save as draft" next to "Save and activate".
Pass the dropdown's MenuItems through actionMenu. menuButtonAriaLabel is required, because the chevron button has no visible text.
Basic
The main button's onClick runs the primary action. The chevron opens the items passed in actionMenu.
Example:
function BasicMenuButtonExample() {const { MenuItem, MenuButton } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<MenuButtonmenuButtonAriaLabel="More compose options"actionMenu={<><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></>}onClick={action("Open Menu")}>Open Menu</MenuButton>);}
Primary
Use appearance="primary" for the main call to action. In this example the dropdown changes which action the main button performs, and the button label updates to match.
Example:
function PrimaryMenuButtonExample() {const { MenuItem, MenuButton } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);// The dropdown selects the action; the main button reflects and performs it.const actions = [{ label: "Save as Draft", run: action("Save as Draft") },{ label: "Save and Activate", run: action("Save and Activate") },];const [selected, setSelected] = React.useState(actions[0]);return (<MenuButtonappearance="primary"menuButtonAriaLabel="More save options"actionMenu={<>{actions.map((a) => (<MenuItem key={a.label} onClick={() => setSelected(a)}>{a.label}</MenuItem>))}</>}onClick={(e) => selected.run(e)}>{selected.label}</MenuButton>);}
Secondary
Use appearance="secondary" when the split button is not the main action on the page.
Example:
function SecondaryMenuButtonExample() {const { MenuItem, MenuButton } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<MenuButtonappearance="secondary"menuButtonAriaLabel="More actions"actionMenu={<><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem></>}onClick={action("Open Menu")}>Open Menu</MenuButton>);}
Disabled
Set disabled to disable both halves of the split button.
Example:
function DisabledMenuButtonExample() {const { MenuItem, MenuButton } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<MenuButtonappearance="primary"disabledmenuButtonAriaLabel="More actions"actionMenu={<><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem></>}onClick={action("Open Menu")}>Open Menu</MenuButton>);}
SingleSelect
SingleSelect lets people choose one option from a list. Use it for short to medium lists where typing to search isn't needed. For long lists, use SingleCombobox or SingleSearchableSelect.
Pass your items as data, along with an itemToString function. Every item needs a unique id. Wrap the select in a FormField so it gets a visible label.
Basic
Pass your items as data and an itemToString function. placeholder is shown until something is selected.
Example:
function BasicSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book" isInvalid error="Please pick a book.">{({ id, isInvalid }) => (<SingleSelectid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Select a book"/>)}</FormField></div>);}
Empty
A select with no data. Disable the select when there is nothing to choose from, rather than opening an empty list.
Example:
function EmptySingleSelectExample() {const { SingleSelect } = MenuV3;const { itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={"placeholder"}disableddata={[]}itemToString={() => ""}placeholder=""/>)}</FormField></div>);}
Disabled
Set disabled to prevent people from opening the select.
Example:
function DisabledSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"disabled/>)}</FormField></div>);}
Include placeholder item
Set includePlaceholderItem to add the placeholder as the first option in the list, so people can clear their choice by selecting it again.
Example:
function IncludePlaceholderItemSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"includePlaceholderItem/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"/>)}</FormField></div>);}
Radio
Set inputType="radio" to show a radio indicator next to each option instead of the default check icon.
Example:
function RadioSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"inputType="radio"/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemId to preselect an item while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"defaultSelectedItemId="book-3"/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemId and onSelectedItemIdChange. Most single selection menus should control the selection state, so the selected value can be saved, validated or reset by your app.
Example:
function ControlledSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedId, setSelectedId] = React.useState("book-1");const selected = books.find((b) => b.id === selectedId);return (<divstyle={{display: "flex",flexDirection: "column",gap: 16,widows: "300px",}}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"selectedItemId={selectedId}onSelectedItemIdChange={(id) => setSelectedId(id)}/>)}</FormField><div style={{ fontSize: 13 }}>Selected: <strong>{selected?.title ?? "none"}</strong></div><div style={{ display: "flex", gap: 8 }}><button onClick={() => setSelectedId(null)}>Clear</button><button onClick={() => setSelectedId("book-5")}>Select 1984</button></div></div>);}
Custom renderSelection
Use renderSelection to change how the selected item appears in the closed trigger, independently of how options look in the list.
Example:
function CustomSelectionSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"renderSelection={(item) => (<span><span style={{ fontWeight: "bold" }}>{item.title}</span><spanstyle={{ fontSize: "11px", color: "#888", marginLeft: "6px" }}>by {item.author}</span></span>)}/>)}</FormField></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsSingleSelectExample() {const { SingleSelect } = MenuV3;const { booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Select a book"/>)}</FormField></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineSingleSelectExample() {const { SingleSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick a book">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"fullWidth={false}/>)}</FormField>);}
MultiSelect
MultiSelect lets people choose several options from a list, with a checkbox next to each one. Use it for short to medium lists where typing to search isn't needed.
Selection is tracked by item id through selectedItemIds / defaultSelectedItemIds.
Basic
Pass your items as data and an itemToString function. People can check as many options as they need, and the menu stays open between choices.
Example:
function BasicMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}placeholder="Select books..."/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormFieldlabel="Pick books"isInvaliderror="Please pick at least one book.">{({ id, isInvalid }) => (<MultiSelectid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Select books..."/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemIds to preselect items while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."defaultSelectedItemIds={["book-1", "book-3"]}/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemIds and onSelectedItemIdsChange. Control the selection when your app needs to save, validate or reset the chosen items.
Example:
function ControlledMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedIds, setSelectedIds] = React.useState(["book-2"]);const selected = books.filter((b) => selectedIds.includes(b.id));return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."selectedItemIds={selectedIds}onSelectedItemIdsChange={(ids) => setSelectedIds(ids)}/>)}</FormField><div style={{ fontSize: 13 }}>Selected:{" "}<strong>{selected.length > 0? selected.map((b) => b.title).join(", "): "none"}</strong></div><div style={{ display: "flex", gap: 8 }}><button onClick={() => setSelectedIds([])}>Clear all</button><button onClick={() => setSelectedIds(["book-5", "book-7"])}>Select 1984 & Meditations</button></div></div>);}
Custom renderSelection
Use renderSelection to replace how the selected items are summarized in the closed trigger, for example as a count or a comma-separated list.
Example:
function CustomSelectionMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."renderSelection={(selected) => {if (selected.length === 0)return <span style={{ color: "#888" }}>Select books...</span>;const first = itemToString(selected[0]);const extra =selected.length > 1 ? ` (+${selected.length - 1})` : "";return (<span>{first}{extra}</span>);}}/>)}</FormField></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Select books..."/>)}</FormField></div>);}
Remove selected items
Set removeSelectedItems to hide items from the list once they are selected. They appear only as selections until they are removed.
Example:
function RemoveSelectedItemsMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books..."removeSelectedItems/>)}</FormField></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineMultiSelectExample() {const { MultiSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick books">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}placeholder="Select books..."fullWidth={false}/>)}</FormField>);}
SingleCombobox
SingleCombobox is a text input that filters a list as people type, and lets them choose one option. Use it for long lists or when people already know what they are looking for.
Basic
Pass your items as data and an itemToString function. Typing filters the list by each item's string value.
Example:
function BasicSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book" isInvalid error="Please pick a book.">{({ id, isInvalid }) => (<SingleComboboxid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."/>)}</FormField></div>);}
Radio
Set inputType="radio" to show a radio indicator next to each option instead of the default check icon.
Example:
function RadioSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."inputType="radio"/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemId to preselect an item while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."defaultSelectedItemId="book-3"/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemId and onSelectedItemIdChange. Most single selection menus should control the selection state, so the selected value can be saved, validated or reset by your app.
Example:
function ControlledSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedId, setSelectedId] = React.useState("book-1");const selected = books.find((b) => b.id === selectedId);return (<divstyle={{display: "flex",flexDirection: "column",gap: 16,width: "300px",}}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."selectedItemId={selectedId}onSelectedItemIdChange={(id) => setSelectedId(id)}/>)}</FormField><div style={{ fontSize: 13 }}>Selected: <strong>{selected?.title ?? "none"}</strong></div><div style={{ display: "flex", gap: 8 }}><button onClick={() => setSelectedId(null)}>Clear</button><button onClick={() => setSelectedId("book-5")}>Select 1984</button></div></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Virtualized
Set isVirtualized for long lists. Only the rows in view are rendered, so lists with thousands of items stay fast. This example has 1,000 books.
Example:
function VirtualizedSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."isVirtualized/>)}</FormField></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."fullWidth={false}/>)}</FormField>);}
Custom search (title + author)
Pass a filter function to change how typed text matches items. This example matches on the author as well as the title. Pass filter={null} to turn filtering off, for example when results come from the server.
Example:
function CustomSearchSingleComboboxExample() {const { SingleCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search by title or author..."filter={(item, query) => {const book = item;const q = query.toLowerCase();return (book.title.toLowerCase().includes(q) ||book.author.toLowerCase().includes(q));}}/>)}</FormField></div>);}
MultiCombobox
MultiCombobox is a text input that filters a list as people type, and lets them choose several options. Selections appear as removable tokens in the input. It supports grouping, select all, virtualization, loading states and creating new items.
Basic
Pass your items as data and an itemToString function. Each selected item becomes a token in the input. Remove a token with its × button.
Example:
function BasicMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemIds to preselect items while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."defaultSelectedItemIds={["book-1", "book-3"]}/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemIds and onSelectedItemIdsChange, and the typed text with inputValue and onInputValueChange. Control both when your app needs to save, validate or reset them.
Example:
function ControlledMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedIds, setSelectedIds] = React.useState(["book-2"]);const [inputValue, setInputValue] = React.useState("");const selected = books.filter((b) => selectedIds.includes(b.id));return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."selectedItemIds={selectedIds}onSelectedItemIdsChange={(ids) => setSelectedIds(ids)}inputValue={inputValue}onInputValueChange={setInputValue}/>)}</FormField><div style={{ fontSize: 13 }}>Input: <strong>"{inputValue}"</strong></div><div style={{ fontSize: 13 }}>Selected:{" "}<strong>{selected.length > 0? selected.map((b) => b.title).join(", "): "none"}</strong></div><div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}><button onClick={() => setSelectedIds([])}>Clear all</button><button onClick={() => setSelectedIds(["book-5", "book-7"])}>Select 1984 & Meditations</button><button onClick={() => setInputValue("War")}>Set input to "War"</button></div></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Selectable headings
With groupBy and selectHeadings, each group heading becomes a checkbox that selects or clears every item in that group.
Example:
function SelectableHeadingsMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}selectHeadings/>)}</FormField></div>);}
Disabled group headings
With selectHeadings, use isGroupHeaderDisabled to turn off the heading checkbox for specific groups.
Example:
function DisabledGroupHeadingsMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}selectHeadingsisGroupHeaderDisabled={(groupName) => groupName === "Harper Lee"}/>)}</FormField></div>);}
Select all
Set selectAll to add a "Select all" option at the top of the list. Pass a string instead of true to change its label.
Example:
function SelectAllMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "600px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."selectHeadingsselectAll/>)}</FormField></div>);}
Select all with grouping
selectAll can be combined with groupBy and selectHeadings, giving people a way to select everything, a whole group, or individual items.
Example:
function SelectAllWithGroupingMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "600px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}selectHeadingsselectAll/>)}</FormField></div>);}
Select all (localized label)
Pass a string to selectAll to change the label of the select all option. Always pass a translated label in the product.
Example:
function SelectAllLocalizedMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "600px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."selectAll="Tout sélectionner"/>)}</FormField></div>);}
Virtualized
Set isVirtualized for long lists. Only the rows in view are rendered, so lists with thousands of items stay fast. This example has 1,000 books.
Example:
function VirtualizedMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."selectHeadingsisVirtualized/>)}</FormField></div>);}
Max tokens
Set maxTokens to limit how many tokens are shown in the input. The rest are collapsed into a "+N" count, which keeps the input from growing too tall.
Example:
function MaxTokensMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."defaultSelectedItemIds={["book-1","book-2","book-3","book-4","book-5",]}maxTokens={2}/>)}</FormField></div>);}
Large book set with max tokens
maxTokens, selectAll and isVirtualized together handle very large lists where people may select hundreds of items.
Example:
function LargeBookSetWithMaxTokensMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."isVirtualizedselectAllmaxTokens={5}/>)}</FormField></div>);}
Loading
Set isLoading while results are being fetched. The list shows a loading message (customizable with loadingText) instead of options.
Example:
function LoadingMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={[]}itemToString={itemToString}placeholder="Search books..."isLoading/>)}</FormField></div>);}
Loading (toggle)
Toggle isLoading to see how the menu moves between the loading state and loaded results, as it would during a real fetch.
Example:
function LoadingWithToggleMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;const [isLoading, setIsLoading] = React.useState(true);const data = isLoading ? [] : books;return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={data}itemToString={itemToString}placeholder="Search books..."isLoading={isLoading}/>)}</FormField><button onClick={() => setIsLoading((v) => !v)}>{isLoading ? "Finish loading" : "Set loading"}</button></div>);}
Remove selected items
Set removeSelectedItems to hide items from the list once they are selected. They appear only as selections until they are removed.
Example:
function RemoveSelectedItemsMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."removeSelectedItems/>)}</FormField></div>);}
Virtualized + select all
Virtualization, grouped headings and select all can be combined, which keeps very large grouped lists usable.
Example:
function VirtualizedSelectAllMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."groupBy={(book) => book.author}selectHeadingsselectAllisVirtualized/>)}</FormField></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."fullWidth={false}/>)}</FormField>);}
Custom search (title + author)
Pass a filter function to change how typed text matches items. This example matches on the author as well as the title. Pass filter={null} to turn filtering off, for example when results come from the server.
Example:
function CustomSearchMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Search by title or author..."filter={(item, query) => {const book = item;const q = query.toLowerCase();return (book.title.toLowerCase().includes(q) ||book.author.toLowerCase().includes(q));}}/>)}</FormField></div>);}
Creatable
Set creatable to let people add an option that isn't in the list. When they choose the create option, onCreate receives the typed text and must return the new item. Add that item to your data.
Example:
function CreatableMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;const [bookList, setBookList] = React.useState(books);return (<div style={{ width: "300px" }}><FormField label="Pick or create books">{({ id }) => (<MultiComboboxid={id}data={bookList}itemToString={itemToString}placeholder="Search or create..."isVirtualizedcreatableonCreate={(value) => {const newBook = {id: `book-custom-${value.toLowerCase().replace(/\s+/g, "-")}`,title: value,author: "Unknown",};setBookList((prev) => [...prev, newBook]);return newBook;}}/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormFieldlabel="Pick books"isInvaliderror="Please pick at least one book.">{({ id, isInvalid }) => (<MultiComboboxid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Required
Set required to mark the field as required for forms and assistive technology.
Example:
function RequiredMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books" required>{({ id, required }) => (<MultiComboboxid={id}required={required}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField></div>);}
Large book set with max tokens (in modal)
The same large, virtualized list with maxTokens and selectAll, inside a Modal.
Example:
function LargeBookSetWithMaxTokensInModalMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<Modaltitle="Pick some books"modalTrigger={<Button appearance="primary">Open Modal</Button>}closeButtonAriaLabel="Close"><ModalBody><div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Search books..."isVirtualizedselectAllmaxTokens={5}/>)}</FormField></div></ModalBody><ModalFooter cancelButton={<Button>Cancel</Button>} /></Modal>);}
Escape Key in Modal (a11y)
Inside a Modal, pressing Escape closes the open dropdown and returns focus to the input, leaving the modal open. Pressing Escape again closes the modal.
Example:
function EscapeKeyInModalMultiComboboxExample() {const { MultiCombobox } = MenuV3;const { books, itemToString } = menuV3Data;return (<Modaltitle="Pick some books"modalTrigger={<Button appearance="primary">Open Modal</Button>}closeButtonAriaLabel="Close"><ModalBody><div style={{ width: "300px", padding: "16px 0" }}><FormField label="Pick books">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField><p style={{ marginTop: 16, fontSize: 14 }}><strong>To verify:</strong> open the combobox, press{" "}<kbd>Escape</kbd>. The dropdown should close and focus should returnto the input. The modal should remain open. Press <kbd>Escape</kbd>{" "}again to close the modal.</p></div></ModalBody><ModalFooter cancelButton={<Button>Cancel</Button>} /></Modal>);}
SingleSearchableSelect
SingleSearchableSelect looks like a regular select when closed. When it opens, a search field appears at the top of the list. Use it when you want the compact look of a select but the list is long enough to need search.
Set the search field's placeholder with searchPlaceholder.
Basic
Pass your items as data and an itemToString function. The trigger shows placeholder, and the search field inside the popup shows searchPlaceholder.
Example:
function BasicSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book" isInvalid error="Please pick a book.">{({ id, isInvalid }) => (<SingleSearchableSelectid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemId to preselect an item while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."defaultSelectedItemId="book-3"/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemId and onSelectedItemIdChange. Most single selection menus should control the selection state, so the selected value can be saved, validated or reset by your app.
Example:
function ControlledSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedId, setSelectedId] = React.useState("book-1");const selected = books.find((b) => b.id === selectedId);return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."selectedItemId={selectedId}onSelectedItemIdChange={(id) => setSelectedId(id)}/>)}</FormField><div style={{ fontSize: 13 }}>Selected: <strong>{selected?.title ?? "none"}</strong></div><div style={{ display: "flex", gap: 8 }}><button onClick={() => setSelectedId(null)}>Clear</button><button onClick={() => setSelectedId("book-5")}>Select 1984</button></div></div>);}
Custom renderSelection
Use renderSelection to change how the selected item appears in the closed trigger, independently of how options look in the list.
Example:
function CustomSelectionSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."renderSelection={(item) => (<span><span style={{ fontWeight: "bold" }}>{item.title}</span><spanstyle={{ fontSize: "11px", color: "#888", marginLeft: "6px" }}>by {item.author}</span></span>)}/>)}</FormField></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Include placeholder item
Set includePlaceholderItem to add the placeholder as the first option in the list, so people can clear their choice by selecting it again.
Example:
function IncludePlaceholderItemSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."includePlaceholderItem/>)}</FormField></div>);}
Include placeholder item (virtualized)
includePlaceholderItem also works with isVirtualized lists.
Example:
function IncludePlaceholderItemVirtualizedSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, largeBookSet, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={largeBookSet}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."includePlaceholderItemisVirtualized/>)}</FormField></div>);}
Virtualized
Set isVirtualized for long lists. Only the rows in view are rendered, so lists with thousands of items stay fast. This example has 1,000 books.
Example:
function VirtualizedSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search books..."isVirtualized/>)}</FormField></div>);}
Loading
Set isLoading while results are being fetched. The list shows a loading message (customizable with loadingText) instead of options.
Example:
function LoadingSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={[]}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."isLoading/>)}</FormField></div>);}
Loading (toggle)
Toggle isLoading to see how the menu moves between the loading state and loaded results, as it would during a real fetch.
Example:
function LoadingWithToggleSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;const [isLoading, setIsLoading] = React.useState(true);const data = isLoading ? [] : books;return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={data}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."isLoading={isLoading}/>)}</FormField><button onClick={() => setIsLoading((v) => !v)}>{isLoading ? "Finish loading" : "Set loading"}</button></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."fullWidth={false}/>)}</FormField>);}
Custom search (title + author)
Pass a filter function to change how typed text matches items. This example matches on the author as well as the title. Pass filter={null} to turn filtering off, for example when results come from the server.
Example:
function CustomSearchSingleSearchableSelectExample() {const { SingleSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick a book">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select a book"searchPlaceholder="Search by title or author..."filter={(item, query) => {const book = item;const q = query.toLowerCase();return (book.title.toLowerCase().includes(q) ||book.author.toLowerCase().includes(q));}}/>)}</FormField></div>);}
MultiSearchableSelect
MultiSearchableSelect looks like a regular select when closed, with a search field inside the popup, and lets people choose several options. Selected items are summarized in the trigger.
Basic
Pass your items as data and an itemToString function. Selected items are summarized in the trigger, and the list stays open while people choose.
Example:
function BasicMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Invalid
Pass isInvalid to show the error state. Pair it with an error on the surrounding FormField so people know what to fix.
Example:
function InvalidMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormFieldlabel="Pick books"isInvaliderror="Please pick at least one book.">{({ id, isInvalid }) => (<MultiSearchableSelectid={id}isInvalid={isInvalid}data={books}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Custom render item
Use renderItem to control how each option looks in the list. Here each book shows its title in bold with the author beside it. itemToString is still used for the accessible name and for filtering.
Example:
function CustomRenderItemMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Grouped
Pass a groupBy function to organize options under headings. Every item that returns the same string is placed in the same group.
Example:
function GroupedMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."groupBy={(book) => book.author}/>)}</FormField></div>);}
Default selected
Use defaultSelectedItemIds to preselect items while letting the component manage selection itself (uncontrolled).
Example:
function DefaultSelectedMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."defaultSelectedItemIds={["book-1", "book-3"]}/>)}</FormField></div>);}
Controlled
Control the selection with selectedItemIds and onSelectedItemIdsChange. Control the selection when your app needs to save, validate or reset the chosen items.
Example:
function ControlledMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;const [selectedIds, setSelectedIds] = React.useState(["book-2"]);const selected = books.filter((b) => selectedIds.includes(b.id));return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."selectedItemIds={selectedIds}onSelectedItemIdsChange={(ids) => setSelectedIds(ids)}/>)}</FormField><div style={{ fontSize: 13 }}>Selected:{" "}<strong>{selected.length > 0? selected.map((b) => b.title).join(", "): "none"}</strong></div><div style={{ display: "flex", gap: 8 }}><button onClick={() => setSelectedIds([])}>Clear all</button><button onClick={() => setSelectedIds(["book-5", "book-7"])}>Select 1984 & Meditations</button></div></div>);}
Custom renderSelection
Use renderSelection to replace how the selected items are summarized in the closed trigger, for example as a count or a comma-separated list.
Example:
function CustomSelectionMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."renderSelection={(selected) => {if (selected.length === 0) return "Select books";const first = itemToString(selected[0]);const extra =selected.length > 1 ? ` (+${selected.length - 1})` : "";return (<span style={{ fontSize: 13 }}>{first}{extra}</span>);}}/>)}</FormField></div>);}
Custom customRenderSelections
Use customRenderSelections to change the text used for each selected item in the trigger summary, without replacing the whole summary like renderSelection does.
Example:
function CustomRenderSelectionsMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."customRenderSelections={(item) => `${item.title} (${item.author})`}/>)}</FormField></div>);}
Disabled items
Set disabled: true on any item in data to keep it visible but unselectable.
Example:
function DisabledItemsMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, booksWithDisabled, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={booksWithDisabled}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."/>)}</FormField></div>);}
Selectable headings
With groupBy and selectHeadings, each group heading becomes a checkbox that selects or clears every item in that group.
Example:
function SelectableHeadingsMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."groupBy={(book) => book.author}selectHeadings/>)}</FormField></div>);}
Select all
Set selectAll to add a "Select all" option at the top of the list. Pass a string instead of true to change its label.
Example:
function SelectAllMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."groupBy={(book) => book.author}selectHeadingsselectAll/>)}</FormField></div>);}
Virtualized
Set isVirtualized for long lists. Only the rows in view are rendered, so lists with thousands of items stay fast. This example has 1,000 books.
Example:
function VirtualizedMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."groupBy={(book) => book.author}selectHeadingsisVirtualized/>)}</FormField></div>);}
Max selections
Set maxSelections to limit how many selected items are listed in the trigger. The rest are summarized as a "+N" count.
Example:
function MaxSelectionsMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."defaultSelectedItemIds={["book-1","book-2","book-3","book-4","book-5",]}maxSelections={2}/>)}</FormField></div>);}
Remove selected items
Set removeSelectedItems to hide items from the list once they are selected. They appear only as selections until they are removed.
Example:
function RemoveSelectedItemsMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."removeSelectedItems/>)}</FormField></div>);}
Virtualized + select all
Virtualization, grouped headings and select all can be combined, which keeps very large grouped lists usable.
Example:
function VirtualizedSelectAllMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, largeBookSet, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={largeBookSet}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search books..."groupBy={(book) => book.author}selectHeadingsselectAllisVirtualized/>)}</FormField></div>);}
Loading
Set isLoading while results are being fetched. The list shows a loading message (customizable with loadingText) instead of options.
Example:
function LoadingMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={[]}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."isLoading/>)}</FormField></div>);}
Loading (toggle)
Toggle isLoading to see how the menu moves between the loading state and loaded results, as it would during a real fetch.
Example:
function LoadingWithToggleMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;const [isLoading, setIsLoading] = React.useState(true);const data = isLoading ? [] : books;return (<divstyle={{width: "300px",display: "flex",flexDirection: "column",gap: 16,}}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={data}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."isLoading={isLoading}/>)}</FormField><button onClick={() => setIsLoading((v) => !v)}>{isLoading ? "Finish loading" : "Set loading"}</button></div>);}
Inline (fullWidth=false)
By default the trigger fills its container. Set fullWidth={false} to size the trigger to its content, which is useful inline with text or in toolbars.
Example:
function InlineMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;return (<FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."fullWidth={false}/>)}</FormField>);}
Custom search (title + author)
Pass a filter function to change how typed text matches items. This example matches on the author as well as the title. Pass filter={null} to turn filtering off, for example when results come from the server.
Example:
function CustomSearchMultiSearchableSelectExample() {const { MultiSearchableSelect } = MenuV3;const { books, itemToString, renderItem } = menuV3Data;return (<div style={{ width: "300px" }}><FormField label="Pick books">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}renderItem={renderItem}placeholder="Select books"searchPlaceholder="Search by title or author..."filter={(item, query) => {const book = item;const q = query.toLowerCase();return (book.title.toLowerCase().includes(q) ||book.author.toLowerCase().includes(q));}}/>)}</FormField></div>);}
In a Modal or PeekIn
All of these menus render their popups into a portal positioned relative to the trigger, so they work inside overlays like Modal and PeekIn without being clipped.
V2 Modal
Every select and combobox inside a Modal, next to standard inputs, so you can compare them. The same fields also render below the trigger, outside the modal.
Example:
function V2ModalInaModalorPeekInExample() {const { SingleSelect, MultiSelect, SingleCombobox, MultiCombobox, SingleSearchableSelect, MultiSearchableSelect } = MenuV3;const { books, itemToString } = menuV3Data;function AllComponents() {return (<div style={{ display: "flex", flexDirection: "column", gap: 4 }}><FormField label="Input">{({ id }) => (<Inputid={id}name="input"value="Some Value"placeholder="Select a book"/>)}</FormField><FormField label="Token Input">{({ id }) => (<TokenInputid={id}name="token-input"tokens={[{ id: "token", value: "Token" }]}value="Some Value"placeholder="Select a book"/>)}</FormField><FormField label="Token Input2">{({ id }) => (<TokenInputid={id}name="token-input"tokens={[]}value="Some Value"placeholder="Select a book"/>)}</FormField><FormField label="Single Select">{({ id }) => (<SingleSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"/>)}</FormField><FormField label="Multi Select">{({ id }) => (<MultiSelectid={id}data={books}itemToString={itemToString}placeholder="Select books"/>)}</FormField><FormField label="Single Combobox">{({ id }) => (<SingleComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField><FormField label="Multi Combobox">{({ id }) => (<MultiComboboxid={id}data={books}itemToString={itemToString}placeholder="Search books..."/>)}</FormField><FormField label="Single Searchable Select">{({ id }) => (<SingleSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select a book"searchPlaceholder="Search books..."/>)}</FormField><FormField label="Multi Searchable Select">{({ id }) => (<MultiSearchableSelectid={id}data={books}itemToString={itemToString}placeholder="Select books"searchPlaceholder="Search books..."/>)}</FormField></div>);}return (<><Modaltitle="Pick some books"modalTrigger={<Button appearance="primary">Open V2 Modal</Button>}closeButtonAriaLabel="Close"><ModalBody><AllComponents /></ModalBody><ModalFooter cancelButton={<Button>Cancel</Button>} /></Modal><AllComponents /></>);}
Action Menu in PeekIn
An ActionMenu with groups and a submenu inside a PeekIn.
Example:
function ActionMenuInPeekInInaModalorPeekInExample() {const { ActionMenu, MenuGroup, MenuItem, MenuSub, MenuSubTrigger } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);const [open, setOpen] = React.useState(false);return (<><Button appearance="primary" onClick={() => setOpen(true)}>Open PeekIn</Button><PeekIn open={open} onOpenChange={setOpen} closeButtonAriaLabel="Close"><PeekInHeader title="Actions"><ActionMenutrigger={<Button appearance="pill">More actions</Button>}><MenuItem onClick={action("Edit")}>Edit</MenuItem><MenuItem onClick={action("Duplicate")}>Duplicate</MenuItem><MenuGroup label="Danger"><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></MenuGroup></ActionMenu></PeekInHeader><PeekInContent><div style={{ display: "flex", flexDirection: "column", gap: 8 }}><ActionMenutrigger={<Button appearance="secondary">Basic Menu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></ActionMenu><ActionMenutrigger={<Button appearance="secondary">Menu with Groups</Button>}><MenuGroup label="Actions" showSeparator><MenuItem onClick={action("Edit")}>Edit</MenuItem><MenuItem onClick={action("Duplicate")}>Duplicate</MenuItem></MenuGroup><MenuGroup label="Danger"><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></MenuGroup></ActionMenu><ActionMenutrigger={<Button appearance="secondary">Menu with Submenu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuSub trigger={<MenuSubTrigger>Share</MenuSubTrigger>}><MenuItem onClick={action("Share to Twitter")}></MenuItem><MenuItem onClick={action("Share to LinkedIn")}></MenuItem><MenuSubtrigger={<MenuSubTrigger>More platforms</MenuSubTrigger>}><MenuItem onClick={action("Share to Facebook")}></MenuItem><MenuItem onClick={action("Share to Instagram")}></MenuItem></MenuSub></MenuSub><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></ActionMenu></div></PeekInContent><PeekInPanel title="Details"><div style={{ display: "flex", flexDirection: "column", gap: 8 }}><ActionMenutrigger={<Button appearance="secondary">Panel Action Menu</Button>}><MenuItem onClick={action("Pin")}>Pin</MenuItem><MenuItem onClick={action("Add to report")}>Add to report</MenuItem><MenuGroup label="Danger" showSeparator><MenuItem onClick={action("Remove")}>Remove</MenuItem></MenuGroup></ActionMenu></div></PeekInPanel></PeekIn></>);}
Action Menu in V2 Modal
An ActionMenu with groups and a submenu inside a Modal.
Example:
function ActionMenuInModalInaModalorPeekInExample() {const { ActionMenu, MenuGroup, MenuItem, MenuSub, MenuSubTrigger } = MenuV3;const action = (label) => (...args) => console.log(label, ...args);return (<Modaltitle="Actions"modalTrigger={<Button appearance="primary">Open Modal</Button>}closeButtonAriaLabel="Close"><ModalBody><div style={{ display: "flex", flexDirection: "column", gap: 8 }}><ActionMenutrigger={<Button appearance="secondary">Basic Menu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuItem onClick={action("View Messages")}>View Messages</MenuItem><MenuItem onClick={action("Analyze post")}>Analyze post</MenuItem></ActionMenu><ActionMenutrigger={<Button appearance="secondary">Menu with Groups</Button>}><MenuGroup label="Actions" showSeparator><MenuItem onClick={action("Edit")}>Edit</MenuItem><MenuItem onClick={action("Duplicate")}>Duplicate</MenuItem></MenuGroup><MenuGroup label="Danger"><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></MenuGroup></ActionMenu><ActionMenutrigger={<Button appearance="secondary">Menu with Submenu</Button>}><MenuItem onClick={action("Compose")}>Compose</MenuItem><MenuSub trigger={<MenuSubTrigger>Share</MenuSubTrigger>}><MenuItem onClick={action("Share to Twitter")}>Twitter</MenuItem><MenuItem onClick={action("Share to LinkedIn")}></MenuItem><MenuSubtrigger={<MenuSubTrigger>More platforms</MenuSubTrigger>}><MenuItem onClick={action("Share to Facebook")}></MenuItem><MenuItem onClick={action("Share to Instagram")}></MenuItem></MenuSub></MenuSub><MenuItem onClick={action("Delete")} disabled>Delete</MenuItem></ActionMenu></div></ModalBody><ModalFooter cancelButton={<Button>Close</Button>} /></Modal>);}