View as Markdown

Menubar

A horizontal menu bar with dropdown menus, similar to desktop application menus.

100%
Loading...
<div class="flex items-start justify-center" style="min-height: 320px">
    <twig:Menubar class="w-72">
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>File</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Item>New Tab<twig:Menubar:Shortcut>⌘T</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item>New Window<twig:Menubar:Shortcut>⌘N</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item disabled>New Incognito Window</twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Sub>
                    <twig:Menubar:SubTrigger>
                        <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                            Share
                            <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4" aria-hidden="true" />
                        </twig:Button>
                    </twig:Menubar:SubTrigger>
                    <twig:Menubar:SubContent>
                        <twig:Menubar:Item>Email link</twig:Menubar:Item>
                        <twig:Menubar:Item>Messages</twig:Menubar:Item>
                        <twig:Menubar:Item>Notes</twig:Menubar:Item>
                    </twig:Menubar:SubContent>
                </twig:Menubar:Sub>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Print...<twig:Menubar:Shortcut>⌘P</twig:Menubar:Shortcut></twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Edit</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Item>Undo<twig:Menubar:Shortcut>⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item>Redo<twig:Menubar:Shortcut>⇧⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Sub>
                    <twig:Menubar:SubTrigger>
                        <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                            Find
                            <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4" aria-hidden="true" />
                        </twig:Button>
                    </twig:Menubar:SubTrigger>
                    <twig:Menubar:SubContent>
                        <twig:Menubar:Item>Search the web</twig:Menubar:Item>
                        <twig:Menubar:Separator />
                        <twig:Menubar:Item>Find...</twig:Menubar:Item>
                        <twig:Menubar:Item>Find Next</twig:Menubar:Item>
                        <twig:Menubar:Item>Find Previous</twig:Menubar:Item>
                    </twig:Menubar:SubContent>
                </twig:Menubar:Sub>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Cut</twig:Menubar:Item>
                <twig:Menubar:Item>Copy</twig:Menubar:Item>
                <twig:Menubar:Item>Paste</twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>View</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content class="w-44">
                <twig:Menubar:CheckboxItem name="bookmarks">Bookmarks Bar</twig:Menubar:CheckboxItem>
                <twig:Menubar:CheckboxItem name="urls" checked>Full URLs</twig:Menubar:CheckboxItem>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Reload<twig:Menubar:Shortcut>⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item disabled>Force Reload<twig:Menubar:Shortcut>⇧⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Toggle Fullscreen</twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Hide Sidebar</twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Profiles</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:RadioGroup name="profile">
                    <twig:Menubar:RadioItem value="andy">Andy</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="benoit" checked>Benoit</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="luis">Luis</twig:Menubar:RadioItem>
                </twig:Menubar:RadioGroup>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Edit...</twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Add Profile...</twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
    </twig:Menubar>
</div>

Installation

php bin/console ux:install menubar --kit shadcn

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.24.0 symfony/ux-icons symfony/ux-twig-component:^3.5 tales-from-a-dev/twig-tailwind-extra:^1.3.0

Copy the following file(s) into your app:

assets/controllers/menubar_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    static targets = ['menu', 'trigger'];

    connect() {
        this._closeOnOutsideClick = this._closeOnOutsideClick.bind(this);
        this.closeAll();
    }

    disconnect() {
        document.removeEventListener('click', this._closeOnOutsideClick, true);
    }

    toggle(event) {
        const menu = this._menuFor(event.currentTarget);
        if (!menu) return;

        if (menu.dataset.state === 'open') {
            this.closeAll();
        } else {
            this._open(menu);
        }
    }

    // Once a menu is open, hovering another trigger switches to its menu; while closed, hovering does nothing.
    enter(event) {
        if (!this._hasOpenMenu()) return;

        const menu = this._menuFor(event.currentTarget);
        if (menu && menu.dataset.state !== 'open') {
            this._open(menu);
        }
    }

    closeAll() {
        this.menuTargets.forEach((menu) => (menu.dataset.state = 'closed'));
        this.triggerTargets.forEach((trigger) => trigger.setAttribute('aria-expanded', 'false'));
        document.removeEventListener('click', this._closeOnOutsideClick, true);
    }

    _open(menu) {
        this.menuTargets.forEach((other) => (other.dataset.state = other === menu ? 'open' : 'closed'));
        this.triggerTargets.forEach((trigger) =>
            trigger.setAttribute('aria-expanded', menu.contains(trigger) ? 'true' : 'false')
        );
        document.addEventListener('click', this._closeOnOutsideClick, true);
    }

    _hasOpenMenu() {
        return this.menuTargets.some((menu) => menu.dataset.state === 'open');
    }

    _menuFor(trigger) {
        return this.menuTargets.find((menu) => menu.contains(trigger)) || null;
    }

    _closeOnOutsideClick(event) {
        if (!this.element.contains(event.target)) {
            this.closeAll();
        }
    }
}
assets/controllers/menubar_sub_controller.js
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
    static values = {
        openDelay: { type: Number, default: 0 },
        closeDelay: { type: Number, default: 0 },
    };

    connect() {
        this.openTimeout = null;
        this.closeTimeout = null;
        this.element.dataset.state = 'closed';
    }

    disconnect() {
        this.#clearTimeouts();
    }

    show() {
        this.#clearTimeouts();
        this.openTimeout = setTimeout(() => {
            this.element.dataset.state = 'open';
            this.openTimeout = null;
        }, this.openDelayValue);
    }

    hide() {
        this.#clearTimeouts();
        this.closeTimeout = setTimeout(() => {
            this.element.dataset.state = 'closed';
            this.closeTimeout = null;
        }, this.closeDelayValue);
    }

    #clearTimeouts() {
        if (this.openTimeout) {
            clearTimeout(this.openTimeout);
            this.openTimeout = null;
        }
        if (this.closeTimeout) {
            clearTimeout(this.closeTimeout);
            this.closeTimeout = null;
        }
    }
}
templates/components/Menubar.html.twig
{# @block content A list of `Menubar:Menu` children. #}
<div
    role="menubar"
    data-slot="menubar"
    {{ attributes.defaults({
        class: 'flex h-9 items-center space-x-1 rounded-md border bg-background p-1 shadow-xs'|tailwind_classes,
        'data-controller': 'menubar',
        'data-action': 'keydown.esc->menubar#closeAll',
    }) }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/CheckboxItem.html.twig
{# @prop name string|null The checkbox form field name. #}
{# @prop checked boolean Whether the checkbox is initially checked. #}
{# @block content The label text. #}
{%- props name = null, checked = false -%}
<label
    data-slot="menubar-checkbox-item"
    {{ attributes.defaults({
        class: 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground'|tailwind_classes,
    }) }}
>
    <input type="checkbox" class="peer sr-only" {% if name %}name="{{ name }}"{% endif %} {% if checked %}checked{% endif %}>
    <span class="absolute start-2 flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
        <twig:ux:icon name="lucide:check" class="size-4" aria-hidden="true" />
    </span>
    {%- block content %}{% endblock -%}
</label>
templates/components/Menubar/Content.html.twig
{# @block content The menu items revealed on hover or focus. #}
<div
    role="menu"
    data-slot="menubar-content"
    {{ attributes.defaults({
        class: 'invisible opacity-0 group-data-[state=open]/menubar-menu:visible group-data-[state=open]/menubar-menu:opacity-100 transition-opacity absolute start-0 top-full z-50 min-w-[12rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md'|tailwind_classes,
    }) }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/Item.html.twig
{# @block content The menu item label. #}
<button
    type="button"
    role="menuitem"
    data-slot="menubar-item"
    {{ attributes.defaults({
        class: 'relative flex w-full cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50'|tailwind_classes,
    }) }}
>
    {%- block content %}{% endblock -%}
</button>
templates/components/Menubar/Menu.html.twig
{# @block content A `Menubar:Trigger` and a `Menubar:Content`. #}
<div
    data-slot="menubar-menu"
    data-menubar-target="menu"
    {{ attributes.defaults({
        class: 'group/menubar-menu relative'|tailwind_classes,
    }) }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/RadioGroup.html.twig
{# @prop name string|null The radio group form field name, shared by all `Menubar:RadioItem` children that omit their own. #}
{# @block content One or more `Menubar:RadioItem` components. #}
{%- props name = null -%}
{%- do provide('menubar.radioGroupName', name) -%}
<div
    role="group"
    data-slot="menubar-radio-group"
    {{ attributes }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/RadioItem.html.twig
{# @prop name string|null The radio form field name. Falls back to the parent `Menubar:RadioGroup`'s `name` when omitted. #}
{# @prop value string The value submitted when this radio is selected. #}
{# @prop checked boolean Whether this radio is initially checked. #}
{# @block content The label text. #}
{%- props name = null, value = '', checked = false -%}
{%- set _menubar_radioGroupName = name ?? inject('menubar.radioGroupName', null) -%}
<label
    data-slot="menubar-radio-item"
    {{ attributes.defaults({
        class: 'relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-none hover:bg-accent hover:text-accent-foreground'|tailwind_classes,
    }) }}
>
    <input type="radio" class="peer sr-only" {% if _menubar_radioGroupName %}name="{{ _menubar_radioGroupName }}"{% endif %} value="{{ value }}" {% if checked %}checked{% endif %}>
    <span class="absolute start-2 flex size-4 items-center justify-center opacity-0 peer-checked:opacity-100">
        <twig:ux:icon name="lucide:circle" class="size-2 fill-current" aria-hidden="true" />
    </span>
    {%- block content %}{% endblock -%}
</label>
templates/components/Menubar/Separator.html.twig
<div
    role="separator"
    data-slot="menubar-separator"
    {{ attributes.defaults({
        class: '-mx-1 my-1 h-px bg-muted'|tailwind_classes,
    }) }}
></div>
templates/components/Menubar/Shortcut.html.twig
{# @block content The shortcut label, e.g. "⌘T". #}
<span
    data-slot="menubar-shortcut"
    {{ attributes.defaults({
        class: 'ml-auto text-xs tracking-widest text-muted-foreground'|tailwind_classes,
    }) }}
>
    {%- block content %}{% endblock -%}
</span>
templates/components/Menubar/Sub.html.twig
{# @prop openDelay number Delay in milliseconds before showing the sub-menu. #}
{# @prop closeDelay number Delay in milliseconds before hiding the sub-menu. #}
{# @block content A `Menubar:SubTrigger` and a `Menubar:SubContent`. #}
{%- props openDelay = 0, closeDelay = 0 -%}
<div
    data-slot="menubar-sub"
    data-menubar-sub-open-delay-value="{{ openDelay }}"
    data-menubar-sub-close-delay-value="{{ closeDelay }}"
    {{ attributes.defaults({
        class: 'group/menubar-sub relative'|tailwind_classes,
        'data-controller': 'menubar-sub',
        'data-action': 'mouseenter->menubar-sub#show mouseleave->menubar-sub#hide',
    }) }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/SubContent.html.twig
{# @block content The sub-menu items revealed on hover or focus. #}
<div
    role="menu"
    data-slot="menubar-sub-content"
    {{ attributes.defaults({
        class: 'invisible opacity-0 group-data-[state=open]/menubar-sub:visible group-data-[state=open]/menubar-sub:opacity-100 transition-opacity absolute start-full top-0 z-50 ms-1 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md'|tailwind_classes,
    }) }}
>
    {%- block content %}{% endblock -%}
</div>
templates/components/Menubar/SubTrigger.html.twig
{# @block content The sub-menu trigger label (e.g., a `button`) that opens the sub-menu on hover or focus. #}
{%- set menubar_sub_trigger_attrs = {
    'data-slot': 'menubar-sub-trigger',
    role: 'menuitem',
    tabindex: 0,
    'data-action': 'focus->menubar-sub#show blur->menubar-sub#hide'|html_attr_type('sst'),
} -%}
{%- block content %}{% endblock -%}
templates/components/Menubar/Trigger.html.twig
{# @block content The trigger label (e.g., a `button`) that opens the menu on click. #}
{%- set menubar_trigger_attrs = {
    'data-slot': 'menubar-trigger',
    'data-menubar-target': 'trigger',
    role: 'menuitem',
    tabindex: 0,
    'aria-haspopup': 'menu',
    'aria-expanded': 'false',
    'data-action': 'click->menubar#toggle mouseenter->menubar#enter'|html_attr_type('sst'),
} -%}
{%- block content %}{% endblock -%}

Usage

<twig:Menubar>
    <twig:Menubar:Menu>
        <twig:Menubar:Trigger>
            <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>File</twig:Button>
        </twig:Menubar:Trigger>
        <twig:Menubar:Content>
            <twig:Menubar:Item>New Tab</twig:Menubar:Item>
            <twig:Menubar:Item>New Window</twig:Menubar:Item>
        </twig:Menubar:Content>
    </twig:Menubar:Menu>
    <twig:Menubar:Menu>
        <twig:Menubar:Trigger>
            <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Edit</twig:Button>
        </twig:Menubar:Trigger>
        <twig:Menubar:Content>
            <twig:Menubar:Item>Undo</twig:Menubar:Item>
            <twig:Menubar:Item>Redo</twig:Menubar:Item>
        </twig:Menubar:Content>
    </twig:Menubar:Menu>
</twig:Menubar>

Examples

Checkbox Items

Use Menubar:CheckboxItem for options that can be toggled on and off. Set the checked prop to make an item checked initially.

100%
Loading...
<div class="flex items-start justify-center" style="min-height: 280px">
    <twig:Menubar class="w-72">
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>View</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content class="w-64">
                <twig:Menubar:CheckboxItem name="bookmarks-bar">Always Show Bookmarks Bar</twig:Menubar:CheckboxItem>
                <twig:Menubar:CheckboxItem name="full-urls" checked>Always Show Full URLs</twig:Menubar:CheckboxItem>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Reload<twig:Menubar:Shortcut>⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item disabled>Force Reload<twig:Menubar:Shortcut>⇧⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Format</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:CheckboxItem name="strikethrough" checked>Strikethrough</twig:Menubar:CheckboxItem>
                <twig:Menubar:CheckboxItem name="code">Code</twig:Menubar:CheckboxItem>
                <twig:Menubar:CheckboxItem name="superscript">Superscript</twig:Menubar:CheckboxItem>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
    </twig:Menubar>
</div>

Radio Group

Wrap Menubar:RadioItem components in a Menubar:RadioGroup so only one can be selected at a time. The group's name is shared with every item, so you only set it once.

100%
Loading...
<div class="flex items-start justify-center" style="min-height: 240px">
    <twig:Menubar class="w-72">
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Profiles</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:RadioGroup name="profile">
                    <twig:Menubar:RadioItem value="andy">Andy</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="benoit" checked>Benoit</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="luis">Luis</twig:Menubar:RadioItem>
                </twig:Menubar:RadioGroup>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Edit...</twig:Menubar:Item>
                <twig:Menubar:Item>Add Profile...</twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Theme</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:RadioGroup name="theme">
                    <twig:Menubar:RadioItem value="light">Light</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="dark">Dark</twig:Menubar:RadioItem>
                    <twig:Menubar:RadioItem value="system" checked>System</twig:Menubar:RadioItem>
                </twig:Menubar:RadioGroup>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
    </twig:Menubar>
</div>

Submenus

Nest a Menubar:Sub inside a Menubar:Content to build a submenu, with its own Menubar:SubTrigger and Menubar:SubContent.

100%
Loading...
<div class="flex items-start justify-center" style="min-height: 320px">
    <twig:Menubar class="w-72">
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>File</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Sub>
                    <twig:Menubar:SubTrigger>
                        <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                            Share
                            <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4" aria-hidden="true" />
                        </twig:Button>
                    </twig:Menubar:SubTrigger>
                    <twig:Menubar:SubContent>
                        <twig:Menubar:Item>Email link</twig:Menubar:Item>
                        <twig:Menubar:Item>Messages</twig:Menubar:Item>
                        <twig:Menubar:Item>Notes</twig:Menubar:Item>
                    </twig:Menubar:SubContent>
                </twig:Menubar:Sub>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Print...<twig:Menubar:Shortcut>⌘P</twig:Menubar:Shortcut></twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Edit</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Item>Undo<twig:Menubar:Shortcut>⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Item>Redo<twig:Menubar:Shortcut>⇧⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Sub>
                    <twig:Menubar:SubTrigger>
                        <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                            Find
                            <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4" aria-hidden="true" />
                        </twig:Button>
                    </twig:Menubar:SubTrigger>
                    <twig:Menubar:SubContent>
                        <twig:Menubar:Item>Find...</twig:Menubar:Item>
                        <twig:Menubar:Item>Find Next</twig:Menubar:Item>
                        <twig:Menubar:Item>Find Previous</twig:Menubar:Item>
                    </twig:Menubar:SubContent>
                </twig:Menubar:Sub>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>Cut</twig:Menubar:Item>
                <twig:Menubar:Item>Copy</twig:Menubar:Item>
                <twig:Menubar:Item>Paste</twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
    </twig:Menubar>
</div>

With Icons

Add a ux:icon inside a Menubar:Item to pair each entry with an icon.

100%
Loading...
<div class="flex items-start justify-center" style="min-height: 200px">
    <twig:Menubar class="w-72">
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>File</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:file" class="me-2 size-4" />
                    New File<twig:Menubar:Shortcut>⌘N</twig:Menubar:Shortcut>
                </twig:Menubar:Item>
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:folder" class="me-2 size-4" />
                    Open Folder
                </twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:save" class="me-2 size-4" />
                    Save<twig:Menubar:Shortcut>⌘S</twig:Menubar:Shortcut>
                </twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
        <twig:Menubar:Menu>
            <twig:Menubar:Trigger>
                <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>Edit</twig:Button>
            </twig:Menubar:Trigger>
            <twig:Menubar:Content>
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:settings" class="me-2 size-4" />
                    Preferences
                </twig:Menubar:Item>
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:trash" class="me-2 size-4" />
                    Delete
                </twig:Menubar:Item>
                <twig:Menubar:Separator />
                <twig:Menubar:Item>
                    <twig:ux:icon name="lucide:help-circle" class="me-2 size-4" />
                    Help
                </twig:Menubar:Item>
            </twig:Menubar:Content>
        </twig:Menubar:Menu>
    </twig:Menubar>
</div>

RTL

To enable RTL support, set the dir="rtl" attribute on the root element.

100%
Loading...
<div class="flex flex-col items-center gap-6" style="min-height: 480px">
    {# Arabic #}
    <div dir="rtl">
        <twig:Menubar class="w-72">
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>ملف</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:Item>علامة تبويب جديدة<twig:Menubar:Shortcut>⌘T</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item>نافذة جديدة<twig:Menubar:Shortcut>⌘N</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item disabled>نافذة التصفح المتخفي الجديدة</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Sub>
                        <twig:Menubar:SubTrigger>
                            <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                                مشاركة
                                <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
                            </twig:Button>
                        </twig:Menubar:SubTrigger>
                        <twig:Menubar:SubContent>
                            <twig:Menubar:Item>رابط البريد الإلكتروني</twig:Menubar:Item>
                            <twig:Menubar:Item>الرسائل</twig:Menubar:Item>
                            <twig:Menubar:Item>الملاحظات</twig:Menubar:Item>
                        </twig:Menubar:SubContent>
                    </twig:Menubar:Sub>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>طباعة...<twig:Menubar:Shortcut>⌘P</twig:Menubar:Shortcut></twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>تعديل</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:Item>تراجع<twig:Menubar:Shortcut>⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item>إعادة<twig:Menubar:Shortcut>⇧⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Sub>
                        <twig:Menubar:SubTrigger>
                            <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                                بحث
                                <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
                            </twig:Button>
                        </twig:Menubar:SubTrigger>
                        <twig:Menubar:SubContent>
                            <twig:Menubar:Item>البحث على الويب</twig:Menubar:Item>
                            <twig:Menubar:Separator />
                            <twig:Menubar:Item>بحث...</twig:Menubar:Item>
                            <twig:Menubar:Item>البحث التالي</twig:Menubar:Item>
                            <twig:Menubar:Item>البحث السابق</twig:Menubar:Item>
                        </twig:Menubar:SubContent>
                    </twig:Menubar:Sub>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>قص</twig:Menubar:Item>
                    <twig:Menubar:Item>نسخ</twig:Menubar:Item>
                    <twig:Menubar:Item>لصق</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>عرض</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content class="w-44">
                    <twig:Menubar:CheckboxItem name="ar-bookmarks">شريط الإشارات المرجعية</twig:Menubar:CheckboxItem>
                    <twig:Menubar:CheckboxItem name="ar-urls" checked>عناوين URL الكاملة</twig:Menubar:CheckboxItem>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>إعادة تحميل<twig:Menubar:Shortcut>⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item disabled>إعادة تحميل قسري<twig:Menubar:Shortcut>⇧⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>تبديل وضع ملء الشاشة</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>إخفاء الشريط الجانبي</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>الملفات الشخصية</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:RadioGroup name="ar-profile">
                        <twig:Menubar:RadioItem value="andy">Andy</twig:Menubar:RadioItem>
                        <twig:Menubar:RadioItem value="benoit" checked>Benoit</twig:Menubar:RadioItem>
                        <twig:Menubar:RadioItem value="luis">Luis</twig:Menubar:RadioItem>
                    </twig:Menubar:RadioGroup>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>تعديل...</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>إضافة ملف شخصي...</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
        </twig:Menubar>
    </div>

    {# Hebrew #}
    <div dir="rtl">
        <twig:Menubar class="w-72">
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>קובץ</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:Item>כרטיסייה חדשה<twig:Menubar:Shortcut>⌘T</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item>חלון חדש<twig:Menubar:Shortcut>⌘N</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item disabled>חלון גלישה בסתר חדש</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Sub>
                        <twig:Menubar:SubTrigger>
                            <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                                שתף
                                <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
                            </twig:Button>
                        </twig:Menubar:SubTrigger>
                        <twig:Menubar:SubContent>
                            <twig:Menubar:Item>קישור אימייל</twig:Menubar:Item>
                            <twig:Menubar:Item>הודעות</twig:Menubar:Item>
                            <twig:Menubar:Item>הערות</twig:Menubar:Item>
                        </twig:Menubar:SubContent>
                    </twig:Menubar:Sub>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>הדפס...<twig:Menubar:Shortcut>⌘P</twig:Menubar:Shortcut></twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>ערוך</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:Item>בטל<twig:Menubar:Shortcut>⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item>בצע שוב<twig:Menubar:Shortcut>⇧⌘Z</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Sub>
                        <twig:Menubar:SubTrigger>
                            <twig:Button variant="ghost" size="sm" class="w-full justify-start" {{ ...menubar_sub_trigger_attrs }}>
                                מצא
                                <twig:ux:icon name="lucide:chevron-right" class="ms-auto size-4 rtl:rotate-180" aria-hidden="true" />
                            </twig:Button>
                        </twig:Menubar:SubTrigger>
                        <twig:Menubar:SubContent>
                            <twig:Menubar:Item>חפש באינטרנט</twig:Menubar:Item>
                            <twig:Menubar:Separator />
                            <twig:Menubar:Item>מצא...</twig:Menubar:Item>
                            <twig:Menubar:Item>מצא הבא</twig:Menubar:Item>
                            <twig:Menubar:Item>מצא הקודם</twig:Menubar:Item>
                        </twig:Menubar:SubContent>
                    </twig:Menubar:Sub>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>גזור</twig:Menubar:Item>
                    <twig:Menubar:Item>העתק</twig:Menubar:Item>
                    <twig:Menubar:Item>הדבק</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>תצוגה</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content class="w-44">
                    <twig:Menubar:CheckboxItem name="he-bookmarks">סרגל סימניות</twig:Menubar:CheckboxItem>
                    <twig:Menubar:CheckboxItem name="he-urls" checked>כתובות URL מלאות</twig:Menubar:CheckboxItem>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>רענן<twig:Menubar:Shortcut>⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Item disabled>רענן בכוח<twig:Menubar:Shortcut>⇧⌘R</twig:Menubar:Shortcut></twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>החלף מסך מלא</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>הסתר סרגל צד</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
            <twig:Menubar:Menu>
                <twig:Menubar:Trigger>
                    <twig:Button variant="ghost" size="sm" {{ ...menubar_trigger_attrs }}>פרופילים</twig:Button>
                </twig:Menubar:Trigger>
                <twig:Menubar:Content>
                    <twig:Menubar:RadioGroup name="he-profile">
                        <twig:Menubar:RadioItem value="andy">Andy</twig:Menubar:RadioItem>
                        <twig:Menubar:RadioItem value="benoit" checked>Benoit</twig:Menubar:RadioItem>
                        <twig:Menubar:RadioItem value="luis">Luis</twig:Menubar:RadioItem>
                    </twig:Menubar:RadioGroup>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>ערוך...</twig:Menubar:Item>
                    <twig:Menubar:Separator />
                    <twig:Menubar:Item>הוסף פרופיל...</twig:Menubar:Item>
                </twig:Menubar:Content>
            </twig:Menubar:Menu>
        </twig:Menubar>
    </div>
</div>

API Reference

<twig:Menubar>

Block Description
content A list of Menubar:Menu children.

<twig:Menubar:CheckboxItem>

Prop Type Default
name 
string|null null
checked 
boolean false
Block Description
content The label text.

<twig:Menubar:Content>

Block Description
content The menu items revealed on hover or focus.

<twig:Menubar:Item>

Block Description
content The menu item label.

<twig:Menubar:Menu>

Block Description
content A Menubar:Trigger and a Menubar:Content.

<twig:Menubar:RadioGroup>

Prop Type Default
name 
string|null null
Block Description
content One or more Menubar:RadioItem components.

<twig:Menubar:RadioItem>

Prop Type Default
name 
string|null null
value 
string ''
checked 
boolean false
Block Description
content The label text.

<twig:Menubar:Shortcut>

Block Description
content The shortcut label, e.g. "⌘T".

<twig:Menubar:Sub>

Prop Type Default
openDelay 
number 0
closeDelay 
number 0
Block Description
content A Menubar:SubTrigger and a Menubar:SubContent.

<twig:Menubar:SubContent>

Block Description
content The sub-menu items revealed on hover or focus.

<twig:Menubar:SubTrigger>

Block Description
content The sub-menu trigger label (e.g., a button) that opens the sub-menu on hover or focus.

<twig:Menubar:Trigger>

Block Description
content The trigger label (e.g., a button) that opens the menu on click.