QUI React

Menu

A menu is a list of options or commands presented to the user that can be used to perform various operations. Menus are typically presented as a dropdown list, where clicking or hovering over a main menu item reveals the list of options.

import {QMenu} from "@qui/react"

Overview

Our menu component is based on Floating UI, a helper library for creating "floating" elements.

Examples

Showcase

To hide and show the menu, supply an interactive element to the anchor prop. The anchor is used to compute the position of the floating panel. If supplied as an element, event handlers will be applied for triggering the open/close state of the menu on interaction.

It can also be supplied as a function or ref, allowing you to customize based on the state of the panel.

import {ReactNode} from "react"
import {FileText, FolderClosed, LayoutDashboard} from "lucide-react"
import {QButton, QMenu, QMenuItem} from "@qui/react"
export default function Showcase(): ReactNode {
const items: QMenuItem[] = [
{
endIcon: LayoutDashboard,
id: "dashboard",
label: "Dashboard",
},
{
endIcon: FolderClosed,
id: "files",
label: "Files",
},
{
endIcon: FileText,
id: "docs",
label: "Docs",
},
]
return (
<div className="flex justify-center gap-6">
<QMenu
anchor={
<QButton aria-label="Default" color="primary" variant="fill">
Default
</QButton>
}
items={items}
/>
<QMenu
anchor={({open, ...anchorProps}) => (
<QButton
aria-label="Customized"
color="primary"
selected={open}
variant="fill"
{...anchorProps}
>
Customized
</QButton>
)}
items={items}
/>
</div>
)
}

Kitchen Sink

If you intend to render the menu item inline, use the QInlineMenu component instead. The following is an example of an inline menu with a diverse array of items.

The text content of the menu is configured using the label, description, secondaryText, and sectionTitle properties. Each of these properties are configured with reasonable defaults for font size, line height, and font weight. Should you need to customize these attributes, supply a ReactNode with your own styles.

import {ReactNode} from "react"
import {ExternalLink, Link2, Trash} from "lucide-react"
import {Link} from "react-router"
import {QInlineMenu} from "@qui/react"
export default function KitchenSink(): ReactNode {
return (
<div className="grid justify-center">
<QInlineMenu
items={[
{id: "start-icon", label: "Start Icon", startIcon: ExternalLink},
{endIcon: ExternalLink, id: "end-icon", label: "End Icon"},
{description: "Description", id: "text-labels", label: "Label"},
{
description: "Description",
id: "with-secondary-text",
label: "Label",
secondaryText: "Secondary Text",
},
{
description: "Description",
endIcon: Trash,
endIconSize: "s",
id: "with-end-icon",
label: "Label",
secondaryText: "Secondary Text",
},
{disabled: true, id: "disabled", label: "Disabled"},
{
id: "with-separator-and-title",
sectionTitle: "Section Title (with top separator)",
separator: "top",
},
{id: "just the label", label: "Label"},
{
id: "bottom-separator",
label: "Label (with bottom separator)",
separator: "bottom",
},
{
endIcon: ExternalLink,
id: "google-link",
label: "External Link",
render: <a href="https://www.google.com" target="_blank" />,
},
{
endIcon: Link2,
id: "custom-render",
label: "Internal Link",
render: <Link to="#item-root-element" />,
},
]}
/>
</div>
)
}

Item Root Element

Supply a ReactElement to the render prop to override the root element of the menu item. This behavior is useful for creating links.

import {ReactNode} from "react"
import {ExternalLink, Link2} from "lucide-react"
import {Link} from "react-router"
import {QInlineMenu} from "@qui/react"
export default function CustomMenuItem(): ReactNode {
return (
<div className="grid justify-center">
<QInlineMenu
items={[
{
endIcon: ExternalLink,
id: "external-link",
label: "External Link",
render: <a href="https://www.google.com" target="_blank" />,
},
{
endIcon: Link2,
id: "internal-link",
label: "Internal Link (react-router)",
render: <Link to="#render" />,
},
]}
/>
</div>
)
}

Supply the items field to any menu item to render a nested menu.

import {ReactNode} from "react"
import {
ChevronRight,
FileText,
FolderClosed,
LayoutDashboard,
} from "lucide-react"
import {QButton, QMenu} from "@qui/react"
export default function SubMenu(): ReactNode {
return (
<div className="grid justify-center">
<QMenu
anchor={
<QButton aria-label="Action" color="primary" variant="outline">
Action
</QButton>
}
items={[
{
id: "dashboard",
label: "Dashboard",
startIcon: LayoutDashboard,
},
{
endIcon: ChevronRight,
id: "files",
items: [
{
id: "file-1",
label: "File",
},
{
id: "file-2",
label: "File",
},
{
endIcon: ChevronRight,
id: "folder",
items: [
{id: "folder-file-1", label: "File"},
{id: "folder-file-2", label: "File"},
{id: "folder-file-3", label: "File"},
],
label: "Folder",
},
],
label: "Files",
startIcon: FolderClosed,
},
{
id: "docs",
label: "Docs",
startIcon: FileText,
},
]}
/>
</div>
)
}

Anchor as Ref

The anchor can be supplied as a reference to a ReactElement instead. When you do this, the menu's event handlers are not bound, so you'll need to manually control the open state.

import {MouseEvent, ReactNode, useRef, useState} from "react"
import {QAvatar, QButton, QMenu} from "@qui/react"
export default function AnchorRef(): ReactNode {
const anchorRef = useRef(null)
const [open, setOpen] = useState(false)
return (
<div className="flex items-center justify-center gap-6">
<QButton
color="primary"
onClick={(event: MouseEvent) => {
/*
* since the anchor is attached to another element, we must prevent the
* click event from bubbling. Otherwise, the list will immediately close
* from the mouseup event.
*/
event.stopPropagation()
setOpen((prevState) => !prevState)
}}
variant="fill"
>
Action
</QButton>
<QAvatar ref={anchorRef} content="AV" variant="text" />
<QMenu
anchor={anchorRef}
items={[
{
id: "projects",
label: "Projects",
},
{
id: "files",
label: "Files",
},
]}
onOpenChange={(value: boolean) => {
setOpen(value)
}}
open={open}
/>
</div>
)
}

API

QMenuItem

Name & DescriptionOption(s)Default
Unique identifier of the item
string
Optional description shown below the label.
ReactNode
When true, the menu item is disabled
boolean
false
icon positioned after the label
| LucideIcon
| ReactNode
The size of the endIcon. Some icons may not look right at the default size. Provide this input to fine-tune.
| 'xs'
| 's'
| 'm'
| 'l'
| 'xl'
| string
| number
Nested menu items.
QMenuItem[]
The menu item's primary message
ReactNode
Item renderer for overriding the default item
ReactElement
<div />
HTML role attribute applied to the menuitem element. Use this property to override the role of the menuitem.
string
'menuitem'
Label rendered opposite to the main label and description.
ReactNode
A label that renders above the item's content. Best paired with separator 'top' to visually partition sections of the menu.
ReactNode
Draw a separator line (thin border) between this item and other items.
| 'top'
| 'bottom'
| 'both'
The size of the menu item and its contents.
's' | 'm'
'm'
icon positioned before the label.
| LucideIcon
| ReactNode
The size of the startIcon. Some icons may not look right at the default size. Provide this input to fine-tune.
| 'xs'
| 's'
| 'm'
| 'l'
| 'xl'
| string
| number
Type
string
Description
Unique identifier of the item
Description
Optional description shown below the label.
Type
boolean
Default
false
Description
When true, the menu item is disabled
Type
| LucideIcon
| ReactNode
Description
icon positioned after the label
Type
| 'xs'
| 's'
| 'm'
| 'l'
| 'xl'
| string
| number
Description
The size of the endIcon. Some icons may not look right at the default size. Provide this input to fine-tune.
Description
Nested menu items.
Description
The menu item's primary message
Type
ReactElement
Default
<div />
Description
Item renderer for overriding the default item
Type
string
Default
'menuitem'
Description
HTML role attribute applied to the menuitem element. Use this property to override the role of the menuitem.
Description
Label rendered opposite to the main label and description.
Description
A label that renders above the item's content. Best paired with separator 'top' to visually partition sections of the menu.
Type
| 'top'
| 'bottom'
| 'both'
Description
Draw a separator line (thin border) between this item and other items.
Type
's' | 'm'
Default
'm'
Description
The size of the menu item and its contents.
Type
| LucideIcon
| ReactNode
Description
icon positioned before the label.
Type
| 'xs'
| 's'
| 'm'
| 'l'
| 'xl'
| string
| number
Description
The size of the startIcon. Some icons may not look right at the default size. Provide this input to fine-tune.

QMenu

Name & DescriptionOption(s)Default
The interactive element that will be used to compute the position of the floating panel. If supplied as an element, event handlers will be applied for triggering the open/close state of the menu on interaction. Can also be supplied as a function that returns an element, or a reference to a ReactElement.
Related symbols:
| Element
| ((
props: Props,
) => Element)
| RefObject<
HTMLElement | object
>
Menu item array.
QMenuItem[]
The CSS transition-duration of the enter and exit animations. Supply as a number to configure both transitions at once, or supply an object to configure the enter and exit transitions independently.
| number
| {
enter: number
exit: number
}
250
Configure the element shown when arrow is true.
{
className?: string
fill?: string
height?: number
style?: CSSProperties
width?: number
}
The component used for the root node. It can be a React component or element.
| ElementType
| ComponentType
'div'
If true, the menu's first item will automatically gain focus when the component is opened. This is recommended for optimal accessibility.
boolean
true
Ensure that the component remains anchored to its reference element following screen resizes or position changes.
boolean
The className property of the Element interface gets and sets the value of the class attribute of the specified element.
string
The default value when uncontrolled.
boolean
The default selected menu item index.
number
If true, the component is disabled.
boolean
Box shadow severity of the panel, based on the five elevation styles provided by QDS. A higher number means a more severe box shadow. Supply 0 to disable the box shadow.
| 0
| 1
| 2
| 3
| 4
| 5
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
Whether to flip the component's placement to keep it in view.
boolean
true
If true, the component will show when the anchor element is focused. This property is ignored if open is supplied.
boolean
false
If true, the component will show when the anchor element is hovered. This property is ignored if open is supplied.
boolean
false
The controlled id of the component. If not provided, a value will be used as a fallback.
string
Determines whether focus should loop around when navigating past the first or last item. This creates a continuous cycle through the menu items using arrow keys.
boolean
true
The distance between the component and its anchor element.
| number
| {
alignmentAxis?: number
crossAxis?: number
mainAxis?: number
}
0
Callback fired when the active menu item changes.
  • index{number}
    (
    index: number,
    ) => void
    Callback fired when panel visibility changes.
    • openThe updated state.
    • eventThe event that triggered the change.
    • reasonThe cause of the change.
    (
    open: boolean,
    event?: Event,
    reason?:
    | 'ancestor-scroll'
    | 'click'
    | 'escape-key'
    | 'focus'
    | 'hover'
    | 'list-navigation'
    | 'outside-press'
    | 'reference-press'
    | 'safe-polygon',
    ) => void
    The controlled state for the menu. Supplying this value will override hover and focus events.
    boolean
    Describes the placement of the component before FloatingUI has applied all the modifiers that may have flipped or altered the component. The final placement may differ from this input property.
    | 'top-start'
    | 'top'
    | 'top-end'
    | 'bottom-start'
    | 'bottom'
    | 'bottom-end'
    | 'left-start'
    | 'left'
    | 'left-end'
    | 'right-start'
    | 'right'
    | 'right-end'
    'top'
    
    CSS position attribute of the component. Can be "absolute" or "fixed".
    | 'absolute'
    | 'fixed'
    'absolute'
    
    If true, focus will be restored to the component's anchor element on close.
    boolean
    true
    
    HTML role attribute applied to the element that wraps the menu items.
    string
    'menu'
    
    Applies a default size to each menu item in this menu. Note that each menu item's size, if configured, will override this value.
    's' | 'm'
    'm'
    
    The style global attribute contains CSS styling declarations to be applied to the element.
    CSSProperties
    The z-index CSS property sets the z-order of a positioned element and its descendants or flex items. Overlapping elements with a larger z-index cover those with a smaller one.
    | string
    | number
    1000
    
    Type
    | Element
    | ((
    props: Props,
    ) => Element)
    | RefObject<
    HTMLElement | object
    >
    Description
    The interactive element that will be used to compute the position of the floating panel. If supplied as an element, event handlers will be applied for triggering the open/close state of the menu on interaction. Can also be supplied as a function that returns an element, or a reference to a ReactElement.
    Related symbols
    Related symbols:
    Description
    Menu item array.
    Type
    | number
    | {
    enter: number
    exit: number
    }
    Default
    250
    
    Description
    The CSS transition-duration of the enter and exit animations. Supply as a number to configure both transitions at once, or supply an object to configure the enter and exit transitions independently.
    Type
    {
    className?: string
    fill?: string
    height?: number
    style?: CSSProperties
    width?: number
    }
    Description
    Configure the element shown when arrow is true.
    Type
    | ElementType
    | ComponentType
    Default
    'div'
    
    Description
    The component used for the root node. It can be a React component or element.
    Type
    boolean
    Default
    true
    
    Description
    If true, the menu's first item will automatically gain focus when the component is opened. This is recommended for optimal accessibility.
    Type
    boolean
    Description
    Ensure that the component remains anchored to its reference element following screen resizes or position changes.
    Type
    string
    Description
    The className property of the Element interface gets and sets the value of the class attribute of the specified element.
    Type
    boolean
    Description
    The default value when uncontrolled.
    Type
    number
    Description
    The default selected menu item index.
    Type
    boolean
    Description
    If true, the component is disabled.
    Type
    | 0
    | 1
    | 2
    | 3
    | 4
    | 5
    | '0'
    | '1'
    | '2'
    | '3'
    | '4'
    | '5'
    Description
    Box shadow severity of the panel, based on the five elevation styles provided by QDS. A higher number means a more severe box shadow. Supply 0 to disable the box shadow.
    Type
    boolean
    Default
    true
    
    Description
    Whether to flip the component's placement to keep it in view.
    Type
    boolean
    Default
    false
    
    Description
    If true, the component will show when the anchor element is focused. This property is ignored if open is supplied.
    Type
    boolean
    Default
    false
    
    Description
    If true, the component will show when the anchor element is hovered. This property is ignored if open is supplied.
    Type
    string
    Description
    The controlled id of the component. If not provided, a value will be used as a fallback.
    Type
    boolean
    Default
    true
    
    Description
    Determines whether focus should loop around when navigating past the first or last item. This creates a continuous cycle through the menu items using arrow keys.
    Type
    | number
    | {
    alignmentAxis?: number
    crossAxis?: number
    mainAxis?: number
    }
    Default
    0
    
    Description
    The distance between the component and its anchor element.
    Type
    (
    index: number,
    ) => void
    Description
    Callback fired when the active menu item changes.
    • index{number}
      Type
      (
      open: boolean,
      event?: Event,
      reason?:
      | 'ancestor-scroll'
      | 'click'
      | 'escape-key'
      | 'focus'
      | 'hover'
      | 'list-navigation'
      | 'outside-press'
      | 'reference-press'
      | 'safe-polygon',
      ) => void
      Description
      Callback fired when panel visibility changes.
      • openThe updated state.
      • eventThe event that triggered the change.
      • reasonThe cause of the change.
      Type
      boolean
      Description
      The controlled state for the menu. Supplying this value will override hover and focus events.
      Type
      | 'top-start'
      | 'top'
      | 'top-end'
      | 'bottom-start'
      | 'bottom'
      | 'bottom-end'
      | 'left-start'
      | 'left'
      | 'left-end'
      | 'right-start'
      | 'right'
      | 'right-end'
      Default
      'top'
      
      Description
      Describes the placement of the component before FloatingUI has applied all the modifiers that may have flipped or altered the component. The final placement may differ from this input property.
      Type
      | 'absolute'
      | 'fixed'
      Default
      'absolute'
      
      Description
      CSS position attribute of the component. Can be "absolute" or "fixed".
      Type
      boolean
      Default
      true
      
      Description
      If true, focus will be restored to the component's anchor element on close.
      Type
      string
      Default
      'menu'
      
      Description
      HTML role attribute applied to the element that wraps the menu items.
      Type
      's' | 'm'
      Default
      'm'
      
      Description
      Applies a default size to each menu item in this menu. Note that each menu item's size, if configured, will override this value.
      Description
      The style global attribute contains CSS styling declarations to be applied to the element.
      Type
      | string
      | number
      Default
      1000
      
      Description
      The z-index CSS property sets the z-order of a positioned element and its descendants or flex items. Overlapping elements with a larger z-index cover those with a smaller one.