View as Markdown

Tabs

Use the following default tabs component example to show a list of links that the user can navigate from on your website.

Loading...
<twig:Tabs defaultValue="profile" class="max-w-xl w-full">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="profile">Profile</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="dashboard">Dashboard</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">Settings</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="contact">Contact</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="disabled" disabled>Disabled</twig:Tabs:Trigger>
    </twig:Tabs:List>
    <twig:Tabs:Content value="profile">
        <div class="p-4 bg-neutral-secondary text-medium text-body rounded-base w-full">
            <p class="text-sm text-body">This is some placeholder content the <strong class="font-medium text-heading">Profile tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="dashboard">
        <div class="p-4 bg-neutral-secondary text-medium text-body rounded-base w-full">
            <p class="text-sm text-body">This is some placeholder content the <strong class="font-medium text-heading">Dashboard tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="settings">
        <div class="p-4 bg-neutral-secondary text-medium text-body rounded-base w-full">
            <p class="text-sm text-body">This is some placeholder content the <strong class="font-medium text-heading">Settings tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="contact">
        <div class="p-4 bg-neutral-secondary text-medium text-body rounded-base w-full">
            <p class="text-sm text-body">This is some placeholder content the <strong class="font-medium text-heading">Contact tab's associated content</strong>. Clicking another tab will toggle the visibility of this one for the next. The tab JavaScript swaps classes to control the content visibility and styling.</p>
        </div>
    </twig:Tabs:Content>
</twig:Tabs>

Installation

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

Install the following Composer dependencies:

composer require twig/extra-bundle twig/html-extra:^3.12.0 tales-from-a-dev/twig-tailwind-extra:^1.0.0

Copy the following file(s) into your app:

import { Controller } from &#039;@hotwired/stimulus&#039;;

export default class extends Controller {
    static targets = [&#039;trigger&#039;, &#039;tab&#039;];
    static values = { activeTab: String };

    open(e) {
        this.activeTabValue = e.currentTarget.dataset.tabId;
    }

    activeTabValueChanged() {
        this.triggerTargets.forEach((trigger) =&gt; {
            const isActive = trigger.dataset.tabId === this.activeTabValue;
            trigger.dataset.state = isActive ? &#039;active&#039; : &#039;inactive&#039;;
            trigger.ariaSelected = isActive;
        });

        this.tabTargets.forEach((tab) =&gt; {
            tab.dataset.state = tab.dataset.tabId === this.activeTabValue ? &#039;active&#039; : &#039;inactive&#039;;
        });
    }
}
{# @prop defaultValue string Define the open Tabs at initial rendering. #}
{# @prop orientation &#039;horizontal&#039;|&#039;vertical&#039; Define the visual orientation. #}
{# @block content The default block. #}
{%- props defaultValue = &#039;&#039;, orientation = &#039;horizontal&#039; -%}

&lt;div
    data-controller=&quot;tabs&quot;
    data-tabs-active-tab-value=&quot;{{ defaultValue }}&quot;
    data-orientation=&quot;{{ orientation }}&quot;
    class=&quot;{{ (&#039;gap-4 group/tabs flex data-[orientation=horizontal]:flex-col &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {% block content %}{% endblock %}
&lt;/div&gt;
{# @prop value string Unique suffix identifier for generating Tabs internal IDs. #}
{# @block content The default block. #}
{%- props value -%}

{%- set _tab_id = &#039;tab-&#039; ~ value -%}
{%- set _tab_content_id = _tab_id ~ &#039;-description&#039; -%}
{%- set open = defaultValue is same as(value) -%}
&lt;div
    id=&quot;{{ _tab_content_id }}&quot;
    data-tabs-target=&quot;tab&quot;
    data-tab-id=&quot;{{ value }}&quot;
    role=&quot;tabpanel&quot;
    aria-labelledby=&quot;{{ _tab_id }}&quot;
    data-state=&quot;{{ open ? &#039;active&#039; : &#039;inactive&#039; }}&quot;
    class=&quot;{{ (&#039;data-[state=inactive]:hidden &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content %}{% endblock -%}
&lt;/div&gt;
{# @prop variant &#039;default&#039;|&#039;line&#039; The visual style variant. #}
{# @block content The default block. #}
{%- props variant = &#039;default&#039; -%}
{%- set style = html_cva(
    base: &#039;group/tabs-list flex flex-wrap -mb-px group-data-[orientation=vertical]/tabs:space-y-4 group-data-[orientation=vertical]/tabs:flex-col&#039;,
    variants: {
        variant: {
            default: &#039;border-b border-default&#039;,
            line: &#039;border-b border-default&#039;,
        },
    },
) -%}

&lt;ul
    role=&quot;tablist&quot;
    data-variant=&quot;{{ variant }}&quot;
    class=&quot;{{ style.apply({variant: variant}, attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}&gt;
    {% block content %}{% endblock %}
&lt;/ul&gt;
{# @prop value string Unique suffix identifier for generating Tabs internal IDs. #}
{# @block content The default block. #}
{%- props value -%}

{%- set _tab_id = &#039;tab-&#039; ~ value -%}
{%- set _tab_content_id = _tab_id ~ &#039;-description&#039; -%}
{%- set open = defaultValue is same as(value) -%}
&lt;li class=&quot;me-2&quot; role=&quot;presentation&quot;&gt;
    &lt;button
        id=&quot;{{ _tab_id }}&quot;
        data-action=&quot;click-&gt;tabs#open&quot;
        data-tabs-target=&quot;trigger&quot;
        data-tab-id=&quot;{{ value }}&quot;
        role=&quot;tab&quot;
        aria-controls=&quot;{{ _tab_content_id }}&quot;
        aria-selected=&quot;{{ open ? &#039;true&#039; : &#039;false&#039; }}&quot;
        data-state=&quot;{{ open ? &#039;active&#039; : &#039;inactive&#039; }}&quot;
        class=&quot;{{ (&#039;inline-flex items-center p-4 disabled:text-fg-disabled disabled:cursor-not-allowed group-data-[variant=default]/tabs-list:hover:text-heading group-data-[variant=default]/tabs-list:hover:bg-neutral-secondary-soft group-data-[variant=default]/tabs-list:rounded-t-base data-[state=active]:text-fg-brand group-data-[variant=default]/tabs-list:data-[state=active]:bg-neutral-secondary-soft group-data-[variant=line]/tabs-list:hover:text-fg-brand group-data-[variant=line]/tabs-list:border-b group-data-[variant=line]/tabs-list:border-transparent group-data-[variant=line]/tabs-list:hover:border-fg-brand group-data-[variant=line]/tabs-list:data-[state=active]:border-brand group-data-[variant=pill]/tabs-list:rounded-base group-data-[variant=pill]/tabs-list:py-2.5 group-data-[variant=pill]/tabs-list:hover:bg-neutral-secondary-soft group-data-[variant=pill]/tabs-list:hover:text-heading group-data-[variant=pill]/tabs-list:data-[state=active]:bg-brand group-data-[variant=pill]/tabs-list:data-[state=active]:text-white [&amp;_svg:not([class*=\&#039;size-\&#039;])]:size-4 whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start disabled:pointer-events-none [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
        {{ attributes }}
    &gt;
        {%- block content -%}{%- endblock -%}
    &lt;/button&gt;
&lt;/li&gt;

Usage

<twig:Tabs defaultValue="account" class="w-[400px]">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="account">Account</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="password">Password</twig:Tabs:Trigger>
    </twig:Tabs:List>
    <twig:Tabs:Content value="account">Make changes to your account here.</twig:Tabs:Content>
    <twig:Tabs:Content value="password">Change your password here.</twig:Tabs:Content>
</twig:Tabs>

Examples

Tabs with underline

Use this alternative tabs component style with an underline instead of a background when hovering and being active on a certain page.

Loading...
<twig:Tabs defaultValue="dashboard" class="max-w-[800px] w-full">
    <twig:Tabs:List variant="line">
        <twig:Tabs:Trigger value="profile">Profile</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="dashboard">Dashboard</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">Settings</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="contacts">Contacts</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="disabled" disabled>Disabled</twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Tabs with icons

This is an example of the tabs component where you can also use a SVG powered icon to complement the text within the navigational tabs.

Loading...
<twig:Tabs defaultValue="dashboard" class="max-w-[800px] w-full">
    <twig:Tabs:List variant="line">
        <twig:Tabs:Trigger value="profile">
            <twig:ux:icon name="flowbite:user-circle-outline" class="size-4 me-2" aria-hidden="true"/>
            Profile
        </twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="dashboard">
            <twig:ux:icon name="flowbite:grid-outline" class="size-4 me-2" aria-hidden="true"/>
            Dashboard
        </twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">
            <twig:ux:icon name="flowbite:adjustments-vertical-outline" class="size-4 me-2" aria-hidden="true"/>
            Settings
        </twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="contacts">
            <twig:ux:icon name="flowbite:user-headset-outline" class="size-4 me-2" aria-hidden="true"/>
            Contacts
        </twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="disabled" disabled>
            Disabled
        </twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Pills tabs

If you want to use pills as a style for the tabs component you can do so by using this example.

Loading...
<twig:Tabs defaultValue="dashboard" class="max-w-[800px] w-full">
    <twig:Tabs:List variant="pill">
        <twig:Tabs:Trigger value="profile">Profile</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="dashboard">Dashboard</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">Settings</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="contacts">Contacts</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="disabled" disabled>Disabled</twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Vertical

Use this example to show a vertically aligned set of tabs on the left side of the page.

Loading...
<twig:Tabs defaultValue="profile" orientation="vertical" class="max-w-xl w-full">
    <twig:Tabs:List variant="pill">
        <twig:Tabs:Trigger value="profile">Profile</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="dashboard">Dashboard</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">Settings</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="contact">Contact</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="disabled" disabled>Disabled</twig:Tabs:Trigger>
    </twig:Tabs:List>
    <twig:Tabs:Content value="profile">
        <div class="p-6 bg-neutral-secondary text-medium text-body rounded-base w-full h-full">
            <h3 class="text-lg font-semibold text-heading mb-4">Profile Tab</h3>
            <p class="mb-2">This is some placeholder content the Profile tab's associated content, clicking another tab will toggle the visibility of this one for the next.</p>
            <p>The tab JavaScript swaps classes to control the content visibility and styling.</p>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="dashboard">
        <div class="p-6 bg-neutral-secondary text-medium text-body rounded-base w-full h-full">
            <h3 class="text-lg font-semibold text-heading mb-4">Dashboard Tab</h3>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="settings">
        <div class="p-6 bg-neutral-secondary text-medium text-body rounded-base w-full h-full">
            <h3 class="text-lg font-semibold text-heading mb-4">Settings Tab</h3>
        </div>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="contact">
        <div class="p-6 bg-neutral-secondary text-medium text-body rounded-base w-full h-full">
            <h3 class="text-lg font-semibold text-heading mb-4">Contact Tab</h3>
        </div>
    </twig:Tabs:Content>
</twig:Tabs>

API Reference

<twig:Tabs>

Prop Type Default
defaultValue 
string ''
orientation 
'horizontal'|'vertical' 'horizontal'
Block Description
content The default block.

<twig:Tabs:Content>

Prop Type Default
value 
string -
Block Description
content The default block.

<twig:Tabs:List>

Prop Type Default
variant 
'default'|'line' 'default'
Block Description
content The default block.

<twig:Tabs:Trigger>

Prop Type Default
value 
string -
Block Description
content The default block.