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
bin/console ux:install tabs --kit flowbite-4
That's it!
Install the following Composer dependencies:
composer require symfony/ux-icons 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 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.dataset.state = isActive ? 'active' : 'inactive';
trigger.ariaSelected = isActive;
});
this.tabTargets.forEach((tab) => {
tab.dataset.state = tab.dataset.tabId === this.activeTabValue ? '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' -%}
<div
data-controller="tabs"
data-tabs-active-tab-value="{{ defaultValue }}"
data-orientation="{{ orientation }}"
class="{{ ('gap-4 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 _tab_id = 'tab-' ~ value -%}
{%- set _tab_content_id = _tab_id ~ '-description' -%}
{%- set open = defaultValue is same as(value) -%}
<div
id="{{ _tab_content_id }}"
data-tabs-target="tab"
data-tab-id="{{ value }}"
role="tabpanel"
aria-labelledby="{{ _tab_id }}"
data-state="{{ open ? 'active' : 'inactive' }}"
class="{{ ('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 flex flex-wrap -mb-px group-data-[orientation=vertical]/tabs:space-y-4 group-data-[orientation=vertical]/tabs:flex-col',
variants: {
variant: {
default: 'border-b border-default',
line: 'border-b border-default',
},
},
) -%}
<ul
role="tablist"
data-variant="{{ variant }}"
class="{{ style.apply({variant: variant}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}>
{% block content %}{% endblock %}
</ul>
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 _tab_id = 'tab-' ~ value -%}
{%- set _tab_content_id = _tab_id ~ '-description' -%}
{%- set open = defaultValue is same as(value) -%}
<li class="me-2" role="presentation">
<button
id="{{ _tab_id }}"
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-state="{{ open ? 'active' : 'inactive' }}"
class="{{ ('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 [&_svg:not([class*=\'size-\'])]:size-4 whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content -%}{%- endblock -%}
</button>
</li>
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
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
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 |
Tabs:Content
| Prop | Type | Description |
|---|---|---|
value |
string |
Unique suffix identifier for generating Tabs internal IDs |
| Block | Description |
|---|---|
content |
The default block |
Tabs:List
| Prop | Type | Description |
|---|---|---|
variant |
'default'|'line' |
The visual style variant. Defaults to default |
| Block | Description |
|---|---|
content |
The default block |
Tabs:Trigger
| Prop | Type | Description |
|---|---|---|
value |
string |
Unique suffix identifier for generating Tabs internal IDs |
| Block | Description |
|---|---|
content |
The default block |