> Leaf UI 0.6.0 · Package: @ifhover/leaf-ui · Source: b30c3d3a8fa9c5e15d0d0522cebfbd33f60c6b06
> Browser documentation: https://ifhover.github.io/leaf-ui/v/0.6.0/en/components/menu.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/v/0.6.0/en/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/v/0.6.0/en/llms-full.txt.

# Menu

Organize hierarchical navigation.

## Basic usage


Overview

Projects

Design resources

Releases

Team members

Settings

basic.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/en/menu/basic.tsx"
import { Menu } from '@ifhover/leaf-ui';

import { FolderKanban, House, Palette, Rocket, Settings, Users } from 'lucide-react';

export function MenuBasic() {
  return (
    <Menu
      mode="inline"
      defaultSelectedKey="design"
      defaultOpenKeys={['projects']}
      items={[
        { key: 'home', label: 'Overview', icon: <House /> },
        {
          key: 'projects',
          label: 'Projects',
          icon: <FolderKanban />,
          children: [
            { key: 'design', label: 'Design resources', icon: <Palette /> },
            { key: 'release', label: 'Releases', icon: <Rocket /> },
          ],
        },
        { key: 'team', label: 'Team members', icon: <Users /> },
        { key: 'settings', label: 'Settings', icon: <Settings /> },
      ]}
    />
  );
}
```

Expand code

## More examples


Horizontal navigation

Overview

Resources

Settings

Collapsible sidebarToggle sidebar

Overview

Resources

Settings

advanced.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/en/menu/advanced.tsx"
import type { MenuItem } from '@ifhover/leaf-ui';

import { Button, Flex, Menu, Text } from '@ifhover/leaf-ui';

import { BookOpen, Folder, House, Palette, Settings } from 'lucide-react';

import { useState } from 'react';

const items: MenuItem[] = [
  { key: 'home', label: 'Overview', icon: <House /> },
  {
    key: 'resources',
    label: 'Resources',
    icon: <Folder />,
    children: [
      { key: 'components', label: 'Components', icon: <BookOpen /> },
      { key: 'themes', label: 'Themes', icon: <Palette /> },
    ],
  },
  { key: 'settings', label: 'Settings', icon: <Settings /> },
];

export function MenuHorizontal() {
  const [collapsed, setCollapsed] = useState(false);
  return (
    <Flex direction="column" gap={20}>
      <Flex direction="column" gap="sm" align="center">
        <Text tone="muted">Horizontal navigation</Text>
        <Menu mode="horizontal" items={items} defaultSelectedKey="home" />
      </Flex>
      <Flex direction="column" gap="sm" align="center">
        <Text tone="muted">Collapsible sidebar</Text>
        <Button variant="ghost" size="sm" onClick={() => setCollapsed((value) => !value)}>
          Toggle sidebar
        </Button>
        <Menu collapsed={collapsed} items={items} defaultSelectedKey="home" />
      </Flex>
    </Flex>
  );
}
```

Expand code

## Controlled navigation and routing

Keep `selectedKey` in sync with the current route. Use `linkRender` to replace the default anchor with a React Router or Next.js Link, retaining its event handlers, classes and ARIA attributes.


[Overview](#overview)

Resources

[Guides](#guides)

[Settings](#settings)

Current page: overview. The example updates local state; replace the anchor with your router Link and retain its props.

capabilities.tsx

Copy code

```tsx lineNumbers file="../../../src/examples/menu/capabilities.tsx"
import type { MenuItem } from '@ifhover/leaf-ui';

import { Menu, Space, Text } from '@ifhover/leaf-ui';

import { BookOpen, House, Settings } from 'lucide-react';

import { type AnchorHTMLAttributes, cloneElement, isValidElement, type ReactElement, useState } from 'react';

export function MenuRouting() {
  const [selected, setSelected] = useState('overview');
  const items: MenuItem[] = [
    { key: 'overview', label: 'Overview', href: '#overview', icon: <House /> },
    {
      key: 'resources',
      label: 'Resources',
      icon: <BookOpen />,
      children: [
        { key: 'guides', label: 'Guides', href: '#guides' },
        {
          key: 'settings',
          label: 'Settings',
          href: '#settings',
          icon: <Settings />,
        },
      ],
    },
  ];
  return (
    <Space direction="vertical" align="center">
      <Menu
        aria-label={'Application navigation'}
        items={items}
        selectedKey={selected}
        defaultOpenKeys={['resources']}
        onSelect={setSelected}
        linkRender={(_item, link) => {
          if (!isValidElement(link)) return link;
          const element = link as ReactElement<AnchorHTMLAttributes<HTMLAnchorElement>>;
          return cloneElement(element, {
            onClick: (event) => {
              element.props.onClick?.(event);
              event.preventDefault();
            },
          });
        }}
      />
      <Text tone="muted">
        {`Current page: ${selected}. The example updates local state; replace the anchor with your router Link and retain its props.`}
      </Text>
    </Space>
  );
}
```

Expand code

## Usage

The vertical menu defaults to 240px (60px when collapsed); use style.width to customize it. items supports nesting, groups, dividers and links. selectedKey controls the current item and openKeys controls expanded items. Horizontal and collapsed modes display submenus in floating panels.

Submenus animate in both directions, and the sidebar width transitions when collapsed. Closed items are excluded from keyboard navigation. Transitions are disabled when the system requests reduced motion.

Menu items, hover backgrounds and selection highlights share the base radius with buttons. Set `ConfigProvider`'s `theme.borderRadius` to adjust them together, or use `theme.components.Menu.borderRadius` to customize the menu radius.

## Types

Import these types for data, callbacks and refs.

```ts
import type { MenuItem, MenuProps } from '@ifhover/leaf-ui';
```

## MenuItem API

| Property   | Type                                       | Default  | Description                        |
| ---------- | ------------------------------------------ | -------- | ---------------------------------- |
| `key`      | `string`                                   | Required | Required. Unique stable identifier |
| `label`    | `ReactNode`                                | Required | Required. Display label            |
| `icon`     | `ReactNode`                                | —        | Custom icon                        |
| `href`     | `string`                                   | —        | Destination URL                    |
| `disabled` | `boolean`                                  | —        | Disable interaction.               |
| `danger`   | `boolean`                                  | —        | Danger colors                      |
| `children` | `readonly `[`MenuItem`](#menuitem-api)`[]` | —        | Content or child items             |
| `type`     | `'item' \| 'group' \| 'divider'`           | —        | Visual or item type                |

## MenuProps API

| Property             | Type                                                                     | Default      | Description                                                                  |
| -------------------- | ------------------------------------------------------------------------ | ------------ | ---------------------------------------------------------------------------- |
| `items`              | `readonly `[`MenuItem`](#menuitem-api)`[]`                               | Required     | Required. Items; keep keys unique and stable                                 |
| `mode`               | `'vertical' \| 'horizontal' \| 'inline'`                                 | `'vertical'` | Menu orientation and behavior                                                |
| `selectedKey`        | `string`                                                                 | —            | Controlled selected key                                                      |
| `defaultSelectedKey` | `string`                                                                 | `''`         | Initial selected key                                                         |
| `onSelect`           | `(key: string, item: `[`MenuItem`](#menuitem-api)`) => void`             | —            | Select an item                                                               |
| `openKeys`           | `readonly string[]`                                                      | —            | Controlled open keys                                                         |
| `defaultOpenKeys`    | `readonly string[]`                                                      | `[]`         | Initially open keys                                                          |
| `onOpenChange`       | `(keys: string[]) => void`                                               | —            | Open state changes                                                           |
| `collapsed`          | `boolean`                                                                | `false`      | Controlled collapsed state                                                   |
| `itemRender`         | `(item: `[`MenuItem`](#menuitem-api)`, content: ReactNode) => ReactNode` | —            | Customize item content.                                                      |
| `linkRender`         | `(item: `[`MenuItem`](#menuitem-api)`, link: ReactNode) => ReactNode`    | —            | Render a router link while retaining supplied accessibility and event props. |
| `itemClassName`      | `string \| ((item: `[`MenuItem`](#menuitem-api)`) => string)`            | —            | Item class, or a class computed from an item.                                |
