View as Markdown

Dropdown

The dropdown component can be used to show a list of menu items when clicking on an element such as a button and hiding it when focusing outside of the triggering element.

100%
Loading...
<div style="min-height: 304px">
    <twig:Dropdown id="demo" open>
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Sign out</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

Installation

Note

Available since UX Toolkit 3.0.

php bin/console ux:install dropdown --kit flowbite-4

Install the following Composer dependencies:

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

Install the following front-end dependencies:

npm install flowbite
# ...or with Symfony AssetMapper
php bin/console importmap:require flowbite

Copy the following file(s) into your app:

assets/controllers/dropdown_controller.js
import { Controller } from '@hotwired/stimulus';
import { Dropdown } from 'flowbite';

export default class extends Controller {
    dropdown = null;
    static targets = ['trigger', 'content'];
    static values = {
        placement: String,
        triggerType: String,
        open: Boolean,
        delay: Number,
        offsetDistance: Number,
    };

    connect() {
        const options = {
            placement: this.placementValue,
            triggerType: this.triggerTypeValue,
            delay: this.delayValue,
            offsetDistance: this.offsetDistanceValue,
        };
        this.dropdown = new Dropdown(this.contentTarget, this.triggerTarget, options);
        this.dropdown.updateOnShow(() => {
            this.triggerTarget.setAttribute('aria-expanded', 'true');
        });
        this.dropdown.updateOnHide(() => {
            this.triggerTarget.setAttribute('aria-expanded', 'false');
        });

        this._onTriggerKeydown = this._handleTriggerKeydown.bind(this);
        this._onContentKeydown = this._handleContentKeydown.bind(this);
        this.triggerTarget.addEventListener('keydown', this._onTriggerKeydown);
        this.contentTarget.addEventListener('keydown', this._onContentKeydown);

        if (this.openValue) {
            this.dropdown.show();
        }
    }

    disconnect() {
        this.triggerTarget.removeEventListener('keydown', this._onTriggerKeydown);
        this.contentTarget.removeEventListener('keydown', this._onContentKeydown);
        this.dropdown?.destroy();
        this.dropdown = null;
    }

    _getMenuItems() {
        // Get menuitems belonging to this menu level only (not nested submenu contents).
        // Items can be: li > a[role="menuitem"] (Item) or li > div[data-controller] > button[role="menuitem"] (SubTrigger)
        const items = [];
        for (const item of this.contentTarget.querySelectorAll('[role="menuitem"]')) {
            if (item.closest('[data-dropdown-target="content"]') === this.contentTarget) {
                items.push(item);
            }
        }
        return items;
    }

    _focusItem(index) {
        const items = this._getMenuItems();
        if (items.length === 0) return;
        const i = ((index % items.length) + items.length) % items.length;
        items[i].focus();
    }

    _focusFirstItem() {
        this._focusItem(0);
    }

    _focusLastItem() {
        this._focusItem(-1);
    }

    _show(focusFirst = true) {
        this.dropdown.show();
        if (focusFirst) {
            // Small delay to ensure the content is visible before focusing
            requestAnimationFrame(() => this._focusFirstItem());
        }
    }

    _hide() {
        this.dropdown.hide();
        this.triggerTarget.focus();
    }

    _handleTriggerKeydown(event) {
        // If this trigger is a SubTrigger (role="menuitem"), arrow keys should be
        // handled by the parent menu for navigation, not by this submenu's trigger.
        const isSubTrigger = this.triggerTarget.getAttribute('role') === 'menuitem';

        switch (event.key) {
            case 'ArrowDown':
                if (isSubTrigger) return;
                if (!this.dropdown.isVisible()) {
                    event.preventDefault();
                    this._show(true);
                }
                break;
            case 'ArrowUp':
                if (isSubTrigger) return;
                if (!this.dropdown.isVisible()) {
                    event.preventDefault();
                    this.dropdown.show();
                    requestAnimationFrame(() => this._focusLastItem());
                }
                break;
            case 'Enter':
            case ' ':
                if (!this.dropdown.isVisible()) {
                    event.preventDefault();
                    this._show(true);
                }
                break;
            case 'Escape':
                if (this.dropdown.isVisible()) {
                    event.preventDefault();
                    this._hide();
                }
                break;
        }
    }

    _handleContentKeydown(event) {
        // Only handle if the focused element belongs to this menu level
        const items = this._getMenuItems();
        const currentIndex = items.indexOf(document.activeElement);
        if (currentIndex === -1) return;

        // Stop propagation to prevent parent dropdown controllers from intercepting
        event.stopPropagation();

        switch (event.key) {
            case 'ArrowDown':
                event.preventDefault();
                this._focusItem(currentIndex + 1);
                break;
            case 'ArrowUp':
                event.preventDefault();
                this._focusItem(currentIndex - 1);
                break;
            case 'Home':
                event.preventDefault();
                this._focusFirstItem();
                break;
            case 'End':
                event.preventDefault();
                this._focusLastItem();
                break;
            case 'Escape':
                event.preventDefault();
                this._hide();
                break;
            case 'Tab':
                this._hide();
                break;
            case 'ArrowRight':
                // If focused on a SubTrigger, open the submenu
                if (document.activeElement.getAttribute('aria-haspopup') === 'menu') {
                    event.preventDefault();
                    document.activeElement.click();
                    // Focus first item in the submenu after it opens
                    requestAnimationFrame(() => {
                        const subContent = document.activeElement
                            ?.closest('[data-controller="dropdown"]')
                            ?.querySelector('[data-dropdown-target="content"]');
                        if (subContent) {
                            const firstItem = subContent.querySelector('[role="menuitem"]');
                            if (firstItem) firstItem.focus();
                        }
                    });
                }
                break;
            case 'ArrowLeft':
                // If inside a submenu, close it and return focus to the SubTrigger
                this._closeParentSubmenu(event);
                break;
        }
    }

    _closeParentSubmenu(event) {
        // Check if this controller is a submenu (nested inside another dropdown)
        const parentDropdown = this.element.closest('li[role="none"]')?.closest('[data-controller="dropdown"]');
        if (parentDropdown && parentDropdown !== this.element) {
            event.preventDefault();
            this._hide();
            // The _hide() already focuses our trigger, which is the SubTrigger in the parent menu
        }
    }
}
templates/components/Dropdown.html.twig
{%- props
    ## string Unique identifier for generating internal IDs.
    id,
    ## boolean Whether the dropdown is open on initial render.
    open = false,
    ## 'bottom'|'top'|'left'|'right'|'left-start'|'left-end'|'right-start'|'right-end'|'bottom-start'|'bottom-end'|'top-start'|'top-end' The dropdown placement relative to the trigger.
    placement = 'bottom',
    ## 'click'|'hover' The dropdown trigger type.
    triggerType = 'click',
    ## int The dropdown delay.
    delay = 300,
    ## int The dropdown content offset.
    offsetDistance = 10
-%}

{%- set _dropdown_id = 'dropdown-' ~ id -%}
{%- set _dropdown_trigger_id = _dropdown_id ~ '-trigger' -%}

<div
    data-dropdown-placement-value="{{ placement }}"
    data-dropdown-trigger-type-value="{{ triggerType }}"
    data-dropdown-open-value="{{ open ? 'true' : 'false' }}"
    data-dropdown-delay-value="{{ delay }}"
    data-dropdown-offset-distance-value="{{ offsetDistance }}"
    {{ attributes.defaults({
        'data-controller': 'dropdown',
    }) }}
>
    {##- The dropdown structure (Dropdown:Trigger and Dropdown:Content). -#}
    {%- block content %}{% endblock -%}
</div>
templates/components/Dropdown/Content.html.twig
<div
    id="{{ _dropdown_id }}"
    data-dropdown-target="content"
    role="menu"
    aria-labelledby="{{ _dropdown_trigger_id }}"
    {{ attributes.defaults({
        class: 'z-10 bg-neutral-primary-medium border border-default-medium rounded-base shadow-lg w-fit min-w-44 divide-y divide-default-medium hidden'|tailwind_classes,
    }) }}
>
    {##- The dropdown menu content, typically includes `Dropdown:Group`, `Dropdown:Header`. -#}
    {%- block content %}{% endblock -%}
</div>
templates/components/Dropdown/Group.html.twig
<ul
    role="group"
    {{ attributes.defaults({
        class: 'p-2 text-sm text-body font-medium'|tailwind_classes,
    }) }}
>
    {##- The list of `Dropdown:Item` elements. -#}
    {%- block content %}{% endblock -%}
</ul>
templates/components/Dropdown/Header.html.twig
<div
    role="none"
    {{ attributes.defaults({
        class: 'm-2 p-2 text-sm text-body bg-neutral-secondary-strong border-0 rounded'|tailwind_classes,
    }) }}
>
    {##- The header content (e.g., user name, email). -#}
    {%- block content %}{% endblock -%}
</div>
templates/components/Dropdown/Item.html.twig
{%- props
    ## string The item tag.
    as = 'a'
-%}

{% set _attributes_defaults = {} %}
{% if as == 'button' %}
    {% set _attributes_defaults = {type: 'button'} %}
{% endif %}

<li role="none">
    <{{ as }}
        role="menuitem"
        tabindex="-1"
        {{ attributes.defaults(_attributes_defaults).defaults({class: 'inline-flex items-center w-full p-2 text-sm font-medium text-body hover:bg-neutral-tertiary-medium hover:text-heading rounded'|tailwind_classes}) }}
    >
        {##- The item label. -#}
        {%- block content %}{% endblock -%}
    </{{ as }}>
</li>
templates/components/Dropdown/Sub.html.twig
{% if __context is defined and __context.id is defined %}
    {% set id = null %}
{% endif %}

{%- props
    ## string Unique identifier for generating internal IDs.
    id,
    ## 'bottom'|'top'|'left'|'right'|'left-start'|'left-end'|'right-start'|'right-end'|'bottom-start'|'bottom-end'|'top-start'|'top-end' The dropdown placement relative to the trigger.
    placement = 'right-start',
    ## 'click'|'hover' The dropdown trigger type.
    triggerType = 'click',
    ## int The dropdown delay.
    delay = 300,
    ## int The dropdown content offset.
    offsetDistance = 10
-%}

{%- set _dropdown_id = 'dropdown-sub-' ~ id -%}
{%- set _dropdown_trigger_id = _dropdown_id ~ '-trigger' -%}
{##- The dropdown structure (Dropdown:SubTrigger and Dropdown:Content). -#}
{%- set _dropdown_content = block('content') -%}

<li role="none">
    <twig:Dropdown :id="id" :placement="placement" :triggerType="triggerType" :delay="delay" :offsetDistance="offsetDistance" {{ ...attributes }}>
        {{- _dropdown_content|raw -}}
    </twig:Dropdown>
</li>
templates/components/Dropdown/SubTrigger.html.twig
{% set _subtrigger_icons = {
    right: 'flowbite:chevron-right-outline',
    top: 'flowbite:chevron-up-outline',
    bottom: 'flowbite:chevron-down-outline',
} %}
{% set _subtrigger_icon = _subtrigger_icons[placement|split('-')|first] ?? null %}

<button
    data-dropdown-target="trigger"
    id="{{ _dropdown_trigger_id }}"
    type="button"
    role="menuitem"
    tabindex="-1"
    aria-expanded="false"
    aria-haspopup="menu"
    aria-controls="{{ _dropdown_id }}"
    {{ attributes.defaults({
        class: 'inline-flex items-center w-full p-2 text-sm font-medium text-body hover:bg-neutral-tertiary-medium hover:text-heading rounded'|tailwind_classes,
    }) }}
>
    {##- The trigger label that toggles the sub dropdown when clicked. -#}
    {%- block content %}{% endblock -%}
    {% if _subtrigger_icon %}
        <twig:ux:icon :name="_subtrigger_icon" class="size-4 ms-auto" aria-hidden="true" />
    {% endif %}
</button>
templates/components/Dropdown/Trigger.html.twig
{%- set dropdown_trigger_attrs = {
    'data-dropdown-target': 'trigger',
    id: _dropdown_trigger_id,
    'aria-expanded': 'false',
    'aria-controls': _dropdown_id,
} -%}

{##- The trigger element (e.g., a `Button`) that toggles the dropdown when clicked. -#}
{%- block content %}{% endblock -%}

Usage

<twig:Dropdown
    id="string"
    open="true | false"
    placement="right | left | top | bottom | right-start | right-end | left-start | left-end | top-start | top-end | bottom-start | bottom-end"
    triggerType="click | hover"
    delay="300"
    offsetDistance="10"
>
    <twig:Dropdown:Trigger>
        <twig:Button {{ ...dropdown_trigger_attrs }}>
            Dropdown
            <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
        </twig:Button>
    </twig:Dropdown:Trigger>

    <twig:Dropdown:Content>
        <twig:Dropdown:Header>
            <span class="block text-sm text-heading font-medium">Bonnie Green</span>
            <span class="block text-sm truncate">name@flowbite.com</span>
        </twig:Dropdown:Header>
        <twig:Dropdown:Group>
            <twig:Dropdown:Item as="a" href="#">Link item</twig:Dropdown:Item>
            <twig:Dropdown:Item as="button">Button item</twig:Dropdown:Item>
            <twig:Dropdown:Sub
                id="string"
                placement="right | left | top | bottom | right-start | right-end | left-start | left-end | top-start | top-end | bottom-start | bottom-end"
                triggerType="click | hover"
                delay="300"
                offsetDistance="10"
            >
                <twig:Dropdown:SubTrigger>
                    Sub menu
                </twig:Dropdown:SubTrigger>

                <twig:Dropdown:Content>
                    <twig:Dropdown:Group>
                        <twig:Dropdown:Item href="#">Sub item</twig:Dropdown:Item>
                        <twig:Dropdown:Item href="#">Sub item</twig:Dropdown:Item>
                    </twig:Dropdown:Group>
                </twig:Dropdown:Content>
            </twig:Dropdown:Sub>
        </twig:Dropdown:Group>
    </twig:Dropdown:Content>
</twig:Dropdown>

Examples

Dropdown hover

Use the ̀triggerType="{hover|click}" prop options to set whether the dropdown should be shown when hovering or clicking on the trigger element (ie. button).

There’s a 300ms default delay when showing or hiding the dropdown due to UI/UX reasons and how it may affect the interaction with other components on the page. Generally, we recommend using the click method.

100%
Loading...
<div style="min-height: 304px">
    <twig:Dropdown id="hover" triggerType="hover">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Sign out</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

Delay duration

You can use the delay={milliseconds} prop options to set the delay on when to show or hide the dropdown menu when using hover. You may want to use this depending on how the users interact with your interface. In this example we add 500 milliseconds instead of the default 300.

100%
Loading...
<div style="min-height: 304px">
    <twig:Dropdown id="delay" triggerType="hover" delay="500">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Sign out</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

Dropdown divider

You can use multiple Dropdown:Group, the Dropdown:Content add a divider between the groups.

100%
Loading...
<div style="min-height: 504px">
    <twig:Dropdown id="divider">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Sign out</twig:Dropdown:Item>
            </twig:Dropdown:Group>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Separated link</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

With header

Use this example to show extra information outside of the list of menu items inside the dropdown.

100%
Loading...
<div style="min-height: 504px">
    <twig:Dropdown id="header">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Header class="flex items-center space-x-1.5">
                <twig:Avatar>
                    <twig:Avatar:Image src="https://flowbite.com/docs/images/people/profile-picture-5.jpg" alt="Bonnie image" />
                    <twig:Avatar:Fallback>BG</twig:Avatar:Fallback>
                </twig:Avatar>
                <div class="text-sm">
                    <div class="font-medium text-heading">Bonnie Green</div>
                    <div class="truncate text-body">name@flowbite.com</div>
                </div>
                <twig:Badge border="bordered">PRO</twig:Badge>
            </twig:Dropdown:Header>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">
                    <twig:ux:icon name="flowbite:user-outline" class="size-4 me-2" aria-hidden="true" />
                    Dashboard
                </twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">
                    <twig:ux:icon name="flowbite:adjustments-horizontal-outline" class="size-4 me-2" aria-hidden="true" />
                    Settings
                </twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">
                    <twig:ux:icon name="flowbite:lock-outline" class="size-4 me-2" aria-hidden="true" />
                    Privacy
                </twig:Dropdown:Item>
            </twig:Dropdown:Group>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item class="text-fg-danger" href="#">
                    <twig:ux:icon name="flowbite:arrow-left-to-bracket-outline" class="size-4 me-2" aria-hidden="true" />
                    Sign out
                </twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

Multi-level dropdown

Use this example to enable multi-level dropdown menus by adding stacked elements inside of each other.

100%
Loading...
<div style="min-height: 504px">
    <twig:Dropdown id="demo" open>
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown button
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-4" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>

        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Sub id="level1">
                    <twig:Dropdown:SubTrigger>
                        Settings
                    </twig:Dropdown:SubTrigger>

                    <twig:Dropdown:Content>
                        <twig:Dropdown:Group>
                            <twig:Dropdown:Item href="#">Submenu item</twig:Dropdown:Item>
                            <twig:Dropdown:Item href="#">Submenu item</twig:Dropdown:Item>
                        </twig:Dropdown:Group>
                    </twig:Dropdown:Content>
                </twig:Dropdown:Sub>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Sign out</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

With icon

Use the menu icon trigger element on components such as cards as an alternative element to the button.

100%
Loading...
<div style="min-height: 304px">
    <div class="flex justify-center space-x-4 rtl:space-x-reverse">
        <twig:Dropdown id="icon-vt">
            <twig:Dropdown:Trigger>
                <twig:Button variant="ghost" size="icon" {{ ...dropdown_trigger_attrs }}>
                    <twig:ux:icon name="flowbite:dots-vertical-outline" class="size-6" aria-hidden="true"/>
                </twig:Button>
            </twig:Dropdown:Trigger>

            <twig:Dropdown:Content>
                <twig:Dropdown:Group>
                    <twig:Dropdown:Item href="#">
                        Dashboard
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Settings
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Earnings
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Sign out
                    </twig:Dropdown:Item>
                </twig:Dropdown:Group>
            </twig:Dropdown:Content>
        </twig:Dropdown>

        <twig:Dropdown id="icon-hz">
            <twig:Dropdown:Trigger>
                <twig:Button variant="ghost" size="icon" {{ ...dropdown_trigger_attrs }}>
                    <twig:ux:icon name="flowbite:dots-horizontal-outline" class="size-6" aria-hidden="true"/>
                </twig:Button>
            </twig:Dropdown:Trigger>

            <twig:Dropdown:Content>
                <twig:Dropdown:Group>
                    <twig:Dropdown:Item href="#">
                        Dashboard
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Settings
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Earnings
                    </twig:Dropdown:Item>
                    <twig:Dropdown:Item href="#">
                        Sign out
                    </twig:Dropdown:Item>
                </twig:Dropdown:Group>
            </twig:Dropdown:Content>
        </twig:Dropdown>
    </div>
</div>

Placement

You can also use the placement={top|right|bottom|left} prop options to choose the placement of the dropdown menu. By default the positioning is set to the bottom side of the button.

100%
Loading...
<div class="flex flex-wrap items-center gap-4" style="min-height: 304px">
    <twig:Dropdown id="placement-top" placement="top">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown top
                <twig:ux:icon name="flowbite:chevron-up-outline" class="size-6" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>
        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>

    <twig:Dropdown id="placement-right" placement="right">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown right
                <twig:ux:icon name="flowbite:chevron-right-outline" class="size-6" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>
        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>

    <twig:Dropdown id="placement-bottom" placement="bottom">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                Dropdown bottom
                <twig:ux:icon name="flowbite:chevron-down-outline" class="size-6" aria-hidden="true" />
            </twig:Button>
        </twig:Dropdown:Trigger>
        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>

    <twig:Dropdown id="placement-left" placement="left">
        <twig:Dropdown:Trigger>
            <twig:Button {{ ...dropdown_trigger_attrs }}>
                <twig:ux:icon name="flowbite:chevron-left-outline" class="size-6" aria-hidden="true" />
                Dropdown left
            </twig:Button>
        </twig:Dropdown:Trigger>
        <twig:Dropdown:Content>
            <twig:Dropdown:Group>
                <twig:Dropdown:Item href="#">Dashboard</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Settings</twig:Dropdown:Item>
                <twig:Dropdown:Item href="#">Earnings</twig:Dropdown:Item>
            </twig:Dropdown:Group>
        </twig:Dropdown:Content>
    </twig:Dropdown>
</div>

API Reference

<twig:Dropdown>

Prop Type Default
id 
string -
open 
boolean false
placement 
'bottom'|'top'|'left'|'right'|'left-start'|'left-end'|'right-start'|'right-end'|'bottom-start'|'bottom-end'|'top-start'|'top-end' 'bottom'
triggerType 
'click'|'hover' 'click'
delay 
int 300
offsetDistance 
int 10
Block Description
content The dropdown structure (Dropdown:Trigger and Dropdown:Content).

<twig:Dropdown:Content>

Block Description
content The dropdown menu content, typically includes Dropdown:Group, Dropdown:Header.

<twig:Dropdown:Group>

Block Description
content The list of Dropdown:Item elements.

<twig:Dropdown:Header>

Block Description
content The header content (e.g., user name, email).

<twig:Dropdown:Item>

Prop Type Default
as 
string 'a'
Block Description
content The item label.

<twig:Dropdown:Sub>

Prop Type Default
id 
string -
placement 
'bottom'|'top'|'left'|'right'|'left-start'|'left-end'|'right-start'|'right-end'|'bottom-start'|'bottom-end'|'top-start'|'top-end' 'right-start'
triggerType 
'click'|'hover' 'click'
delay 
int 300
offsetDistance 
int 10
Block Description
content The dropdown structure (Dropdown:SubTrigger and Dropdown:Content).

<twig:Dropdown:SubTrigger>

Block Description
content The trigger label that toggles the sub dropdown when clicked.

<twig:Dropdown:Trigger>

Block Description
content The trigger element (e.g., a Button) that toggles the dropdown when clicked.