Tabs
Explore and switch between different views using tabs
Danish Composers
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example">
<h3>Danish Composers</h3>
<Tabs.Root behavior="automatic">
<Tabs.List>
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
Building blocks
The full version
import { component$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
export default component$(() => {
return (
<Tabs.Root selectedIndex={1}>
<Tabs.List>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Tab>Tab 3</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Content 1</Tabs.Panel>
<Tabs.Panel>Content 2</Tabs.Panel>
<Tabs.Panel>Content 3</Tabs.Panel>
</Tabs.Root>
);
});
The short version
import { component$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
export default component$(() => {
return (
<Tabs.Root>
<Tabs.Panel title="Tab 1">Content 1</Tabs.Panel>
<Tabs.Panel title="Tab 2" selected>
Content 2
</Tabs.Panel>
<Tabs.Panel title="Tab 3">Content 3</Tabs.Panel>
</Tabs.Root>
);
});
Vertical
by default, the tabs are horizontal, but you can adjust the underlying behavior to be vertical by setting the vertical
prop to true.
Danish Composers
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<Tabs.Root vertical class="flex flex-wrap gap-5">
<Tabs.List class="flex w-fit flex-col">
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
Disabled
You can disable a tab by setting the disabled
prop to true.
Dad jokes
import { component$, useStore, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
const store = useStore({
tabs: [
{ tab: 'Tab 1', disabled: true },
{ tab: 'Tab 2' },
{ tab: 'Tab 3', disabled: true },
{ tab: 'Tab 4' },
],
});
return (
<>
<div class="four-tabs-example mr-auto w-full">
<h3>Dad jokes</h3>
<Tabs.Root>
<Tabs.List>
{store.tabs.map((tab, i) => (
<Tabs.Tab key={i} disabled={tab.disabled}>
{tab.tab}
</Tabs.Tab>
))}
</Tabs.List>
{store.tabs.map((tab, i) => (
<Tabs.Panel key={i}>{tab.tab} Panel</Tabs.Panel>
))}
</Tabs.Root>
</div>
</>
);
});
Behavior
Automatic
The default behavior of the tabs is manual, which means that the tabs will automatically switch to the next tab when the user hovers over the tab. You can change this behavior by setting the behavior
prop to automatic
.
Danish Composers
(Hover over the tabs)
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<h4>(Hover over the tabs)</h4>
<Tabs.Root behavior="automatic">
<Tabs.List>
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
Manual
You can set the behavior to manual, which means that the tabs will not automatically switch to the next tab when the user presses the right arrow key, and to the previous tab when the user presses the left arrow key.
Danish Composers
(Hover over the tabs - they should not be selected)
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<h4 class="mb-4">(Hover over the tabs - they should not be selected)</h4>
<Tabs.Root behavior="manual">
<Tabs.List>
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
Dynamic
import { component$, useStore, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
const store = useStore({
tabs: ['Tab 1', 'Tab 2', 'Tab 3'],
});
return (
<>
<div class="vertical-tabs-example mr-auto">
<button
class={cn('mb-4 w-48', buttonVariants({ look: 'outline', size: 'sm' }))}
onClick$={() => {
store.tabs.unshift(`Added Tab`);
}}
>
Add Tab before
</button>
<br />
<Tabs.Root vertical class="flex space-x-6">
<Tabs.List class="flex w-fit flex-col">
{store.tabs.map((tab, i) => (
<Tabs.Tab key={i}>
{tab}{' '}
<span
onClick$={() => {
if (store.tabs.length > 1) {
store.tabs.splice(i, 1);
}
}}
>
x
</span>
</Tabs.Tab>
))}
</Tabs.List>
{store.tabs.map((tab, i) => {
return <Tabs.Panel key={i}>{tab} Panel</Tabs.Panel>;
})}
</Tabs.Root>
<button
class={cn('w-48', buttonVariants({ look: 'outline', size: 'sm' }))}
onClick$={() => {
store.tabs.push(`Added Tab`);
}}
>
Add Tab after
</button>
</div>
</>
);
});
import { buttonVariants } from '@qwik-ui/styled';
import { cn } from '@qwik-ui/utils';
onSelectedIndexChange$
You can listen to changes in the selected index by subscribing to the onSelectedIndexChange$
store.
Danish Composers
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
Selected Index: 0
import { component$, useSignal, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
const selectedIndexSig = useSignal(0);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<Tabs.Root
onSelectedIndexChange$={(index) => {
selectedIndexSig.value = index;
}}
>
<Tabs.List>
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
<br />
<p class="mt-4">Selected Index: {selectedIndexSig.value}</p>
</div>
</>
);
});
bind:selectedIndex
You can provide a signal for the selected index with the bind:selectedIndex={yourSignal}
and it will be used directly. This is a more efficient version of onSelectedIndexChange$
.
bind:selectedTabId
You can provide a signal for the selected tab id with the bind:selectedTabId={yourSignal}
and it will be used directly.
💡 You can manually set the tabId
prop on each tab to be able to select any tab by its ID.
Danish Composers
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
Selected Tab Id: id-0
import { component$, useSignal, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
const selectedTabIdSig = useSignal<string | undefined>();
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<Tabs.Root bind:selectedTabId={selectedTabIdSig}>
<Tabs.List>
<Tabs.Tab tabId="id-0">Maria</Tabs.Tab>
<Tabs.Tab tabId="id-1">Carl</Tabs.Tab>
<Tabs.Tab tabId="id-2">Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
<p class="mt-4 text-white">
<strong>Selected Tab Id</strong>: {selectedTabIdSig.value}
</p>
</div>
</>
);
});
Add a "selected" prop
You can add a "selected" prop to the Tab component to make it selected by default.
Danish Composers
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Maria</Tabs.Tab>
<Tabs.Tab selected>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
onClick$
You can pass a custom onClick$
handler.
Danish Composers
(Hover over the tabs - they should not be selected)
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { component$, useStyles$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
import styles from '.././index.css?inline';
export default component$(() => {
useStyles$(styles);
return (
<>
<div class="tabs-example mr-auto">
<h3>Danish Composers</h3>
<h4 class="mb-4">(Hover over the tabs - they should not be selected)</h4>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab
onClick$={(_, el) => {
el.innerHTML = 'Jack';
}}
>
Maria
</Tabs.Tab>
<Tabs.Tab>Carl</Tabs.Tab>
<Tabs.Tab>Ida</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</Tabs.Panel>
<Tabs.Panel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</Tabs.Panel>
</Tabs.Root>
</div>
</>
);
});
Creating reusable Tabs
In order to remove the need for a linking value
prop for each Tab and Tabs.Panel pair, we have chosen to create the Tabs component as an inline component.
By consequence, the Tabs component needs to be aware of its children. If you want to create your custom reusable Tabs.List/Tab/Tabs.Panel components, you will have to pass them to the Tabs component through its Tabs.List
, Tab
, and Tabs.Panel
props:
Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...
Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...
Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...
import { PropsOf, Slot, component$ } from '@builder.io/qwik';
import { Tabs } from '@qwik-ui/headless';
const CustomTabs = (props: PropsOf<typeof Tabs.Root>) => (
<Tabs.Root
{...props}
tabListComponent={CustomTabsList}
tabComponent={CustomTab}
tabPanelComponent={CustomTabsPanel}
/>
);
const CustomTabsList = component$<PropsOf<typeof Tabs.List>>(() => {
return (
<Tabs.List>
<Slot />
</Tabs.List>
);
});
const CustomTab = component$<PropsOf<typeof Tabs.Tab>>(({ ...props }) => {
return (
<Tabs.Tab {...props}>
<span class="text-red-500">Custom</span>
<Slot />
</Tabs.Tab>
);
});
const CustomTabsPanel = component$<PropsOf<typeof Tabs.Panel>>(({ ...props }) => {
return (
<Tabs.Panel {...props}>
<span class="text-red-500">Description:</span>
<Slot />
</Tabs.Panel>
);
});
export default component$(() => {
return (
<div class="tabs-example mr-auto">
<CustomTabs>
<CustomTabsList>
<CustomTab>Tab 1</CustomTab>
<CustomTab>Tab 2</CustomTab>
<CustomTab>Tab 3</CustomTab>
</CustomTabsList>
<CustomTabsPanel>
<p>Maria Theresia Ahlefeldt (16 January 1755 - 20 December 1810) ...</p>
</CustomTabsPanel>
<CustomTabsPanel>
<p>Carl Joachim Andersen (29 April 1847 - 7 May 1909) ...</p>
</CustomTabsPanel>
<CustomTabsPanel>
<p>Ida Henriette da Fonseca (July 27, 1802 - July 6, 1858) ...</p>
</CustomTabsPanel>
</CustomTabs>
</div>
);
});
Accessibility
Keyboard interaction
Key | Description |
---|---|
Tab | |
Shift + Tab | |
ArrowRight | |
ArrowLeft | |
ArrowDown | |
ArrowUp | |
Home | |
PageUp | |
End | |
PageDown |
API
Tabs
Prop | Type | Description |
---|---|---|
behavior | string | Toggle between automatic or manual. The automatic behavior moves between tabs when hover. The manual behavior moves between tabs on click. |
selectedIndex | number | A way to set the selected index programmatically |
selectedTabId | number | A way to set the selected tabId programmatically. The tabId is set by the `key` prop of the Tab or Tabs.Panel |
vertical | boolean | If set to true, the behavior of UpArrow and DownArrow will navigate between tabs vertically instead of horizontally. |
onSelectedIndexChange$ | function (index: number) => void | An event hook that gets notified whenever the selected index changes |
onSelectedTabIdChange$ | function (index: string) => void | An event hook that gets notified whenever the selected tabId changes |
bind:selectedIndex | signal Signal<number | undefined> | A signal that binds the selected index. This is a more efficient version of `selectedIndex` + `onSelectedIndexChange$` |
bind:selectedTabId | signal Signal<string | undefined> | A signal that binds the selected tabId. This is a more efficient version of `selectedTabId` + `onSelectedTabIdChange$` |
tabClass | string | The class name to apply to tabs |
panelClass | string | The class name to apply to panels |
Tab
Prop | Type | Description |
---|---|---|
selectedClassName | string | The class name to apply when the tab is selected |
selected | boolean | Select this tab by default |
disabled | boolean | Set the disabled state of a specific tab |
tabId | string | Set the tab id, can be used with `bind:selectedTabId` to select a tab programmatically |
Tabs.Panel
Prop | Type | Description |
---|---|---|
label | element | Shorthand for `<Tab>{label}</Tab>` |
selected | boolean | Select this tab by default |