Menu
Flexible component for constructing dynamic and customizable menus.
The <Menu> component allows you to define a menu element. It's useful when you want a list with options or actions.
It is structured in two parts <Menu> and <Menu.Item>. The <Menu> contains the trigger and the button per default. You also can use the <Menu.Section> which you can use to separate the menu items from each other.
There is also a companion component called <ActionMenu> which you can use if you want to take some actions. You can have a look on how it works in the examples. It works quite similar to the normal <Menu> component. All you have to add are the <Menu.Item>s.
Anatomy
- Menu trigger: The element, such as a button or icon, that opens or activates the menu.
- Section: Menus are made up of a section or multiple sections.
- Section header: Contains a heading to name a section of a group of similar menu items.
- Menu item: Represent an individual option or action within a Menu component.
Appearance
The appearance of a component can be customized using the variant and size props. These props adjust the visual style and dimensions of the component, available values are based on the active theme.
| Property | Type | Description |
|---|---|---|
variant | default | destructive | ghost | The available variants of this component. |
size | default | small | large | icon | The available sizes of this component. |
Usage
In general, Menus are used to hide less frequently used or advanced options until users specifically need them. This keeps the interface clean and focused on essential elements.
Menus are ideal for interactive UI elements, it is distinct from <Select>, which is meant for form inputs and value selection.
Menus can speed up interactions for advanced users who are already familiar with the application. These users often rely on shortcuts and context-specific actions to work more efficiently.
Write menu label that is clear, concise (max. 24 characters) and avoid unnecessary verbs.
Organise your menu items based on priority, and put the most used items at the start of the menu.
Simple menu with action
In this example you can see a simple <Menu> with some items to select. it is used when you need to provide users with a set of options that will result in a specific action being performed.
import { Menu } from '@marigold/components';export default () => { return ( <Menu label="Actions" onAction={action => alert(`Your action: ${String(action)}`)} > <Menu.Item id="edit">Open in editor</Menu.Item> <Menu.Item id="settings">Settings</Menu.Item> <Menu.Item id="delete" variant="destructive"> Delete </Menu.Item> </Menu> );};Menu with sections
When you have mutliple menu items that can be categorized under different headings. This is particularly useful in complext menus with many options, you can use <Menu.Section> and pass title for the group name.
import { Menu } from '@marigold/components';export default () => { return ( <Menu label="Ticket Services"> <Menu.Section title="Game Day"> <Menu.Item id="match_info">📅 View Match Information</Menu.Item> <Menu.Item id="stadium_guide">🏟️ Stadium Guide</Menu.Item> <Menu.Item id="fan_zone">🎉 Fan Zone Activities</Menu.Item> </Menu.Section> <Menu.Section title="Support"> <Menu.Item id="customer_support">📞 Contact Support</Menu.Item> <Menu.Item id="faq">❓ View FAQs</Menu.Item> <Menu.Item id="feedback">💬 Provide Feedback</Menu.Item> </Menu.Section> </Menu> );};Disabled menu items
In this example the <Menu> has set its prop disabeldKeys. So you can't interact with the <Menu.Item> anymore. Keep in mind, that you have to set an id to the <Menu.Item>.
import { Menu } from '@marigold/components';export default () => { return ( <Menu label="Ticket Options" disabledKeys={['upgrade', 'resell']}> <Menu.Item id="view">👁️ View Ticket Details</Menu.Item> <Menu.Item id="download">📥 Download Ticket</Menu.Item> <Menu.Item id="upgrade">⬆️ Upgrade Seat</Menu.Item> <Menu.Item id="resell">💸 Resell Ticket</Menu.Item> </Menu> );};ActionMenu
The <ActionMenu> is typically used to provide quick, context-specific actions related to an item or interface element. It is particularly useful in scenarios where speed and efficiency in selecting an action are crucial.
In this example, the <ActionMenu> is used within a table to allow users to perform actions like viewing details of a ticket or deleting a ticket. When "View Details" is selected, a dialog displays key information about the selected ticket, including its ID, event name, and status. The "Delete" option removes the ticket from the list.
The example shows that the <ActionMenu> is very similar to the standard <Menu> component, offering flexibility and ease of use in various contexts.
ID | Event Name | Status | Action |
|---|---|---|---|
TCK-001 | Champions League Final | Confirmed | |
TCK-002 | Rock Concert | Pending | |
TCK-003 | Broadway Show | Cancelled |
import { useState } from 'react';import { ActionMenu, Menu, Table } from '@marigold/components';const rows = [ { ticketId: 'TCK-001', eventName: 'Champions League Final', status: 'Confirmed', }, { ticketId: 'TCK-002', eventName: 'Rock Concert', status: 'Pending', }, { ticketId: 'TCK-003', eventName: 'Broadway Show', status: 'Cancelled', },];interface Ticket { ticketId: string; eventName: string; status: 'Cancelled' | 'Pending' | 'Confirmed';}export default () => { const [tickets, setTickets] = useState(rows); const handleViewDetails = (ticket: Ticket) => { alert( `Ticket ID: ${ticket.ticketId}\nEvent: ${ticket.eventName}\nStatus: ${ticket.status}` ); }; return ( <Table aria-label="Data Table" size="compact"> <Table.Header> <Table.Column rowHeader>ID</Table.Column> <Table.Column>Event Name</Table.Column> <Table.Column>Status</Table.Column> <Table.Column>Action</Table.Column> </Table.Header> <Table.Body items={rows}> {tickets.map((ticket: Ticket) => ( <Table.Row key={ticket.ticketId}> <Table.Cell>{ticket.ticketId}</Table.Cell> <Table.Cell>{ticket.eventName}</Table.Cell> <Table.Cell>{ticket.status}</Table.Cell> <Table.Cell> <ActionMenu> <Menu.Item onAction={() => handleViewDetails(ticket)} id="view-details" > View Details </Menu.Item> <Menu.Item variant="destructive" onAction={() => setTickets( tickets.filter( ticketItem => ticketItem.ticketId !== ticket.ticketId ) ) } id="delete" > Delete </Menu.Item> </ActionMenu> </Table.Cell> </Table.Row> ))} </Table.Body> </Table> );};Menu item opens dialog
This Example shows how to open a <Dialog> from a <Menu.Item>. It is useful when a menu action requires additional user input or confirmation. Selecting the item opens a Dialog where users can provide the necessary information or confirm their choice, making the interaction smooth and efficient.
import { MessageCircleHeart, User } from 'lucide-react';import { useState } from 'react';import { Button, Dialog, Menu, TextArea } from '@marigold/components';export default () => { const [open, setDialogOpen] = useState(false); const handleAction = (action: 'profile' | 'feedback') => { switch (action) { case 'profile': alert('Profile opened!'); break; case 'feedback': setDialogOpen(true); break; default: throw new Error(`Unhandled action "${action}"!`); } }; return ( <> <Menu onAction={handleAction} label="User Menu"> <Menu.Item id="profile"> <User /> View Profile </Menu.Item> <Menu.Item id="feedback"> <MessageCircleHeart /> Send Feedback </Menu.Item> </Menu> <Dialog size="xsmall" closeButton open={open} onOpenChange={setDialogOpen} > {({ close }) => ( <> <Dialog.Title>Send Feedback</Dialog.Title> <Dialog.Content> <TextArea placeholder="Your feedback..." rows={4} /> </Dialog.Content> <Dialog.Actions> <Button onPress={close}>Cancel</Button> <Button variant="primary" onPress={close}> Submit </Button> </Dialog.Actions> </> )} </Dialog> </> );};Menu selection mode
Use multiple selection mode when you want to allow users to select multiple options at once, providing flexibility in situations where multiple choices are valid and necessary.
Here you can see how the selectionMode from <Menu> works. In this example the selectionMode is set to multiple. If you open the items you can see a selected item.
Your preferences are : newsletter
import { useState } from 'react';import { Menu } from '@marigold/components';export default () => { const [preferences, setPreferences] = useState(['newsletter']); return ( <> <Menu label="Select Your Preference" selectionMode="multiple" selectedKeys={preferences} onSelectionChange={setPreferences as (keys: any) => void} > <Menu.Item id="newsletter">📧 Subscribe to Newsletter</Menu.Item> <Menu.Item id="offers">💸 Receive Special Offers</Menu.Item> <Menu.Item id="updates">🔔 Get Product Updates</Menu.Item> <Menu.Item id="events">🎉 Event Invitations</Menu.Item> </Menu>{' '} <pre>Your preferences are : {[...preferences].join(', ')}</pre> </> );};Destructive actions
For actions that result in irreversible data loss, such as deleting an item or removing a user account, use the "destructive" variant on menu items to warn the user of the consequences. Always safeguard these actions with a confirmation dialog that clearly explains the outcome and requires an explicit second step to complete the process.
import { useState } from 'react';import { Button, Dialog, Menu } from '@marigold/components';export default () => { const [open, setDialogOpen] = useState(false); return ( <> <Menu label="Settings"> <Menu.Item id="save">Save</Menu.Item> <Menu.Item id="delete" variant="destructive" onAction={() => setDialogOpen(true)} > Delete </Menu.Item> </Menu> <Dialog.Trigger open={open} onOpenChange={setDialogOpen}> <Dialog role="alertdialog" closeButton> <Dialog.Title>Confirm delete</Dialog.Title> <Dialog.Content> Are you sure you want to delete this event? This action cannot be undone. </Dialog.Content> <Dialog.Actions> <Button variant="secondary" slot="close"> Cancel </Button> <Button variant="destructive" slot="close"> Delete </Button> </Dialog.Actions> </Dialog> </Dialog.Trigger> </> );};Props
Menu
Prop
Type
Menu.Item
Prop
Type
Menu.Section
Prop
Type