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
Note
Available since UX Toolkit 2.33.
bin/console ux:install tabs --kit shadcn
That's it!
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 Symfony app:
assets/controllers/tabs_controller.js
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['trigger', 'tab'];
static values = { activeTab: String };
open(e) {
this.activeTabValue = e.currentTarget.dataset.tabId;
}
activeTabValueChanged() {
this.triggerTargets.forEach((trigger) => {
const isActive = trigger.dataset.tabId === this.activeTabValue;
trigger.toggleAttribute('data-active', isActive);
trigger.ariaSelected = isActive;
});
this.tabTargets.forEach((tab) => {
const isActive = tab.dataset.tabId === this.activeTabValue;
tab.toggleAttribute('data-active', isActive);
tab.dataset.state = isActive ? 'active' : 'inactive';
});
}
}
templates/components/Tabs.html.twig
{# @prop defaultValue string define the open Tabs at initial rendering. Defaults to `` #}
{# @prop orientation 'horizontal'|'vertical' define the visual orientation. Defaults to `horizontal` #}
{# @block content The default block #}
{%- props defaultValue = '', orientation = 'horizontal' -%}
{%- do provide('tabs.defaultValue', defaultValue) -%}
<div
data-slot="tabs"
data-controller="tabs"
data-tabs-active-tab-value="{{ defaultValue }}"
data-orientation="{{ orientation }}"
class="{{ ('gap-2 group/tabs flex data-[orientation=horizontal]:flex-col ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{% block content %}{% endblock %}
</div>
templates/components/Tabs/Content.html.twig
{# @prop value string Unique suffix identifier for generating Tabs internal IDs #}
{# @block content The default block #}
{%- props value -%}
{%- set _tabs_defaultValue = inject('tabs.defaultValue', '') -%}
{%- set _tab_id = 'tab-' ~ value -%}
{%- set _tab_content_id = _tab_id ~ '-description' -%}
{%- set open = _tabs_defaultValue is same as(value) -%}
<div
id="{{ _tab_content_id }}"
data-slot="tabs-content"
data-tabs-target="tab"
data-tab-id="{{ value }}"
role="tabpanel"
aria-labelledby="{{ _tab_id }}"
data-active="{{ open ? 'true' : false }}"
data-state="{{ open ? 'active' : 'inactive' }}"
class="{{ ('flex-1 text-sm outline-none data-[state=inactive]:hidden ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/Tabs/List.html.twig
{# @prop variant 'default'|'line' The visual style variant. Defaults to `default` #}
{# @block content The default block #}
{%- props variant = 'default' -%}
{%- set style = html_cva(
base: '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',
variants: {
variant: {
default: 'bg-muted',
line: 'gap-1 bg-transparent',
},
},
) -%}
<nav
data-slot="tabs-list"
role="tablist"
data-variant="{{ variant }}"
class="{{ style.apply({variant: variant}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}>
{% block content %}{% endblock %}
</nav>
templates/components/Tabs/Trigger.html.twig
{# @prop value string Unique suffix identifier for generating Tabs internal IDs #}
{# @block content The default block #}
{%- props value -%}
{%- set _tabs_defaultValue = inject('tabs.defaultValue', '') -%}
{%- set _tab_id = 'tab-' ~ value -%}
{%- set _tab_content_id = _tab_id ~ '-description' -%}
{%- set open = _tabs_defaultValue is same as(value) -%}
<button
id="{{ _tab_id }}"
data-slot="tabs-trigger"
data-action="click->tabs#open"
data-tabs-target="trigger"
data-tab-id="{{ value }}"
role="tab"
aria-controls="{{ _tab_content_id }}"
aria-selected="{{ open ? 'true' : 'false' }}"
data-active="{{ open ? 'true' : false }}"
class="{{ ('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 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]: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 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content -%}{%- endblock -%}
</button>
Happy coding!
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
Component Tabs
| Prop | Type | Description |
|---|---|---|
defaultValue |
string |
define the open Tabs at initial rendering. Defaults to `` |
orientation |
'horizontal'|'vertical' |
define the visual orientation. Defaults to horizontal |
| Block | Description |
|---|---|
content |
The default block |
Component Tabs:Content
| Prop | Type | Description |
|---|---|---|
value |
string |
Unique suffix identifier for generating Tabs internal IDs |
| Block | Description |
|---|---|
content |
The default block |
Component Tabs:List
| Prop | Type | Description |
|---|---|---|
variant |
'default'|'line' |
The visual style variant. Defaults to default |
| Block | Description |
|---|---|
content |
The default block |
Component Tabs:Trigger
| Prop | Type | Description |
|---|---|---|
value |
string |
Unique suffix identifier for generating Tabs internal IDs |
| Block | Description |
|---|---|
content |
The default block |