View as Markdown

Tabs

A set of layered sections of content—known as tab panels—that are displayed one at a time.

Loading...
<twig:Tabs defaultValue="overview" class="w-[400px]">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="overview">Overview</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="analytics">Analytics</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="reports">Reports</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings">Settings</twig:Tabs:Trigger>
    </twig:Tabs:List>
    <twig:Tabs:Content value="overview">
        <twig:Card>
            <twig:Card:Header>
                <twig:Card:Title>Overview</twig:Card:Title>
                <twig:Card:Description>
                    View your key metrics and recent project activity. Track progress
                    across all your active projects.
                </twig:Card:Description>
            </twig:Card:Header>
            <twig:Card:Content class="text-muted-foreground text-sm">
                You have 12 active projects and 3 pending tasks.
            </twig:Card:Content>
        </twig:Card>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="analytics">
        <twig:Card>
            <twig:Card:Header>
                <twig:Card:Title>Analytics</twig:Card:Title>
                <twig:Card:Description>
                    Track performance and user engagement metrics. Monitor trends and
                    identify growth opportunities.
                </twig:Card:Description>
            </twig:Card:Header>
            <twig:Card:Content class="text-muted-foreground text-sm">
                Page views are up 25% compared to last month.
            </twig:Card:Content>
        </twig:Card>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="reports">
        <twig:Card>
            <twig:Card:Header>
                <twig:Card:Title>Reports</twig:Card:Title>
                <twig:Card:Description>
                    Generate and download your detailed reports. Export data in
                    multiple formats for analysis.
                </twig:Card:Description>
            </twig:Card:Header>
            <twig:Card:Content class="text-muted-foreground text-sm">
                You have 5 reports ready and available to export.
            </twig:Card:Content>
        </twig:Card>
    </twig:Tabs:Content>
    <twig:Tabs:Content value="settings">
        <twig:Card>
            <twig:Card:Header>
                <twig:Card:Title>Settings</twig:Card:Title>
                <twig:Card:Description>
                    Manage your account preferences and options. Customize your
                    experience to fit your needs.
                </twig:Card:Description>
            </twig:Card:Header>
            <twig:Card:Content class="text-muted-foreground text-sm">
                Configure notifications, security, and themes.
            </twig:Card:Content>
        </twig:Card>
    </twig:Tabs:Content>
</twig:Tabs>

Installation

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

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 symfony/ux-twig-component:^3.1

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.toggleAttribute(&#039;data-active&#039;, isActive);
            trigger.ariaSelected = isActive;
        });

        this.tabTargets.forEach((tab) =&gt; {
            const isActive = tab.dataset.tabId === this.activeTabValue;
            tab.toggleAttribute(&#039;data-active&#039;, isActive);
            tab.dataset.state = isActive ? &#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; -%}
{%- do provide(&#039;tabs.defaultValue&#039;, defaultValue) -%}

&lt;div
    data-slot=&quot;tabs&quot;
    data-controller=&quot;tabs&quot;
    data-tabs-active-tab-value=&quot;{{ defaultValue }}&quot;
    data-orientation=&quot;{{ orientation }}&quot;
    class=&quot;{{ (&#039;gap-2 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 _tabs_defaultValue = inject(&#039;tabs.defaultValue&#039;, &#039;&#039;) -%}

{%- set _tab_id = &#039;tab-&#039; ~ value -%}
{%- set _tab_content_id = _tab_id ~ &#039;-description&#039; -%}
{%- set open = _tabs_defaultValue is same as(value) -%}
&lt;div
    id=&quot;{{ _tab_content_id }}&quot;
    data-slot=&quot;tabs-content&quot;
    data-tabs-target=&quot;tab&quot;
    data-tab-id=&quot;{{ value }}&quot;
    role=&quot;tabpanel&quot;
    aria-labelledby=&quot;{{ _tab_id }}&quot;
    data-active=&quot;{{ open ? &#039;true&#039; : false }}&quot;
    data-state=&quot;{{ open ? &#039;active&#039; : &#039;inactive&#039; }}&quot;
    class=&quot;{{ (&#039;flex-1 text-sm outline-none 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 inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none&#039;,
    variants: {
        variant: {
            default: &#039;bg-muted&#039;,
            line: &#039;gap-1 bg-transparent&#039;,
        },
    },
) -%}

&lt;nav
    data-slot=&quot;tabs-list&quot;
    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;/nav&gt;
{# @prop value string Unique suffix identifier for generating Tabs internal IDs. #}
{# @block content The default block. #}
{%- props value -%}
{%- set _tabs_defaultValue = inject(&#039;tabs.defaultValue&#039;, &#039;&#039;) -%}

{%- set _tab_id = &#039;tab-&#039; ~ value -%}
{%- set _tab_content_id = _tab_id ~ &#039;-description&#039; -%}
{%- set open = _tabs_defaultValue is same as(value) -%}
&lt;button
    id=&quot;{{ _tab_id }}&quot;
    data-slot=&quot;tabs-trigger&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-active=&quot;{{ open ? &#039;true&#039; : false }}&quot;
    class=&quot;{{ (&#039;relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 ltr:has-data-[icon=inline-end]:pr-1 rtl:has-data-[icon=inline-end]:pe-1 ltr:has-data-[icon=inline-start]:pl-1 rtl:has-data-[icon=inline-start]:ps-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&amp;_svg]:pointer-events-none [&amp;_svg]:shrink-0 [&amp;_svg:not([class*=\&#039;size-\&#039;])]:size-4 group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 ltr:group-data-[orientation=vertical]/tabs:after:-right-1 rtl:group-data-[orientation=vertical]/tabs:after:-end-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100 &#039; ~ attributes.render(&#039;class&#039;))|tailwind_merge }}&quot;
    {{ attributes }}
&gt;
    {%- block content -%}{%- endblock -%}
&lt;/button&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

Line

Use the variant="line" prop on Tabs:List for a line style.

Loading...
<twig:Tabs defaultValue="overview">
    <twig:Tabs:List variant="line">
        <twig:Tabs:Trigger value="overview">Overview</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="analytics">Analytics</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="reports">Reports</twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Vertical

Use orientation="vertical" for vertical tabs.

Loading...
<twig:Tabs defaultValue="account" orientation="vertical">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="account">Account</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="password">Password</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="notifications">Notifications</twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Disabled

Loading...
<twig:Tabs defaultValue="home">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="home">Home</twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="settings" disabled>
            Disabled
        </twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

Icons

Loading...
<twig:Tabs defaultValue="preview">
    <twig:Tabs:List>
        <twig:Tabs:Trigger value="preview">
            <twig:ux:icon name="lucide:app-window" />
            Preview
        </twig:Tabs:Trigger>
        <twig:Tabs:Trigger value="code">
            <twig:ux:icon name="lucide:code" />
            Code
        </twig:Tabs:Trigger>
    </twig:Tabs:List>
</twig:Tabs>

RTL

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

Loading...
<div class="flex flex-col gap-8">
    {# Arabic #}
    <twig:Tabs defaultValue="overview" class="w-full max-w-sm" dir="rtl">
        <twig:Tabs:List dir="rtl">
            <twig:Tabs:Trigger value="overview">نظرة عامة</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="analytics">التحليلات</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="reports">التقارير</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="settings">الإعدادات</twig:Tabs:Trigger>
        </twig:Tabs:List>
        <twig:Tabs:Content value="overview">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>نظرة عامة</twig:Card:Title>
                    <twig:Card:Description>
                        عرض مقاييسك الرئيسية وأنشطة المشروع الأخيرة. تتبع التقدم عبر جميع مشاريعك النشطة.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    لديك ١٢ مشروعًا نشطًا و٣ مهام معلقة.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="analytics">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>التحليلات</twig:Card:Title>
                    <twig:Card:Description>
                        تتبع مقاييس الأداء ومشاركة المستخدمين. راقب الاتجاهات وحدد فرص النمو.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    زادت مشاهدات الصفحة بنسبة ٢٥٪ مقارنة بالشهر الماضي.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="reports">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>التقارير</twig:Card:Title>
                    <twig:Card:Description>
                        إنشاء وتنزيل تقاريرك التفصيلية. تصدير البيانات بتنسيقات متعددة للتحليل.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    لديك ٥ تقارير جاهزة ومتاحة للتصدير.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="settings">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>الإعدادات</twig:Card:Title>
                    <twig:Card:Description>
                        إدارة تفضيلات حسابك وخياراته. تخصيص تجربتك لتناسب احتياجاتك.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    تكوين الإشعارات والأمان والسمات.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
    </twig:Tabs>

    {# Hebrew #}
    <twig:Tabs defaultValue="overview" class="w-full max-w-sm" dir="rtl">
        <twig:Tabs:List dir="rtl">
            <twig:Tabs:Trigger value="overview">סקירה כללית</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="analytics">אנליטיקה</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="reports">דוחות</twig:Tabs:Trigger>
            <twig:Tabs:Trigger value="settings">הגדרות</twig:Tabs:Trigger>
        </twig:Tabs:List>
        <twig:Tabs:Content value="overview">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>סקירה כללית</twig:Card:Title>
                    <twig:Card:Description>
                        הצג את המדדים העיקריים שלך ואת הנתונים האחרונים. עקוב אחר ההתקדמות בכל המיזמים.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    יש לך 12 מיזמים נגישים ו-3 משימות ממתינות.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="analytics">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>אנליטיקה</twig:Card:Title>
                    <twig:Card:Description>
                        עקוב אחר ביצועים ומדדי מעורבות משתמשים. זהה מגמות והזדמנויות צמיחה.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    הגידול עמד על 25% בהשוואה לחודש שעבר.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="reports">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>דוחות</twig:Card:Title>
                    <twig:Card:Description>
                        צור והורד את הדוחות המלאים שלך. ייצא נתונים בתבניות שונות לניתוח.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    יש לך 5 דוחות מוכנים וזמינים לייצוא.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
        <twig:Tabs:Content value="settings">
            <twig:Card dir="rtl">
                <twig:Card:Header>
                    <twig:Card:Title>הגדרות</twig:Card:Title>
                    <twig:Card:Description>
                        ערוך את הגדרות החשבון שלך. התאם אישית את החוויה כך שתתאים לצרכיך.
                    </twig:Card:Description>
                </twig:Card:Header>
                <twig:Card:Content class="text-sm text-muted-foreground">
                    הגדר התראות, אבטחה וערכות נושא.
                </twig:Card:Content>
            </twig:Card>
        </twig:Tabs:Content>
    </twig:Tabs>
</div>

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.