SubMenu

SubMenu is an expandable container that holds MenuItem and other SubMenu children. When the sidebar is collapsed (or when Menu’s popover mode is enabled), top-level SubMenus open as floating poppers; otherwise they slide open inline.

Active state cascades automatically in v2 — a SubMenu is marked active whenever any of its descendants is active, with no need for manual active prop management.

<Menu>
  <SubMenu label="Charts" icon={<ChartIcon />} defaultOpen>
    <MenuItem>Pie</MenuItem>
    <MenuItem>Line</MenuItem>
  </SubMenu>
</Menu>

Props

label

The submenu’s label — usually a string, but any React node works.

  • Type: string | ReactNode
  • Default:
<SubMenu label="Charts">…</SubMenu>
<SubMenu label={<span>Charts <Badge>6</Badge></span>}>…</SubMenu>

icon

Icon displayed before the label.

  • Type: ReactNode
  • Default:
<SubMenu label="Charts" icon={<ChartIcon />}>

</SubMenu>

defaultOpen

Initial open state when used uncontrolled.

  • Type: boolean
  • Default: false
Loading demo…
<SubMenu label="Charts" defaultOpen>

</SubMenu>

open

Controlled open state. When set, the SubMenu becomes fully controlled — you own the state and must pair this with onOpenChange.

  • Type: boolean
  • Default:
Loading demo…
const [open, setOpen] = useState(false);
 
<SubMenu label="Charts" open={open} onOpenChange={setOpen}>

</SubMenu>;

active

Forces the active visual state. Note: a SubMenu is also marked active automatically whenever any descendant is active (v2 behavior) — you only need this prop to force the state explicitly.

  • Type: boolean
  • Default: false
<SubMenu label="Charts" active>

</SubMenu>

disabled

Disables the submenu trigger. Sets aria-disabled.

  • Type: boolean
  • Default: false
<SubMenu label="Charts" disabled>

</SubMenu>

prefix

Node rendered between the icon and the label.

  • Type: ReactNode
  • Default:
Loading demo…

suffix

Node rendered after the label — common for badges, counts, or expand affordances.

  • Type: ReactNode
  • Default:
<SubMenu label="Charts" suffix={<Badge>6</Badge>}>

</SubMenu>

accordion

New in v2.

When true, only one of this SubMenu’s direct child SubMenus can be open at a time. The scope is per-level — accordions don’t leak up to ancestors.

  • Type: boolean
  • Default: false
Loading demo…
<SubMenu label="Components" accordion>
  <SubMenu label="Forms">…</SubMenu>
  <SubMenu label="Layout">…</SubMenu>
  <SubMenu label="Display">…</SubMenu>
</SubMenu>

onOpenChange

Called whenever the open state changes — for either controlled or uncontrolled SubMenus.

  • Type: (open: boolean) => void
  • Default:
<SubMenu label="Charts" onOpenChange={(open) => console.log('charts open:', open)}>

</SubMenu>

component

Override the element used for the submenu trigger. Accepts a string tag name or a React element.

  • Type: string | ReactElement
  • Default:
<SubMenu label="Charts" component="div">

</SubMenu>

rootStyles

Emotion CSSObject applied to the SubMenu’s root <li> element.

  • Type: CSSObject
  • Default:
<SubMenu label="Charts" rootStyles={{ borderTop: '1px solid #eee' }}>

</SubMenu>

All props

NameTypeDefaultDescription
labelstring | ReactNodeLabel rendered on the trigger
iconReactNodeIcon displayed before the label
defaultOpenbooleanfalseInitial open state (uncontrolled)
openbooleanControlled open state
activebooleanfalseForced active state (auto-cascades from descendants too)
disabledbooleanfalseDisabled state (adds aria-disabled)
prefixReactNodeNode between the icon and the label
suffixReactNodeNode after the label
accordionbooleanfalsev2. Only one direct-child SubMenu can be open at a time
onOpenChange(open: boolean) => voidCalled when the open state changes
componentstring | ReactElementElement used for the trigger node
rootStylesCSSObjectStyles applied to the SubMenu root