Tabs

Easily create accessible, fully customizable tab interfaces, with robust focus management and keyboard navigation support.

To get started, install Headless UI via npm or yarn:

# npm npm install @headlessui/react # Yarn yarn add @headlessui/react

Tabs are built using the Tab.Group, Tab.List, Tab, Tab.Panels, and Tab.Panel components. By default the first tab is selected, and clicking on any tab or selecting it with the keyboard will activate the corresponding panel.

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

This is a headless component so there are no styles included by default. Instead, the components expose useful information via render props you can use to apply styles yourself.

To style the selected Tab, use the selected render prop, which tells you whether or not that tab is currently selected.

import { Fragment } from 'react' import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab as={Fragment}>
{({ selected }) => (
<button
className={
selected ? 'bg-blue-500 text-white' : 'bg-white text-black'
}
>
Tab 1 </button> )} </Tab> {/* ... */} </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> {/* ... */} </Tab.Panels> </Tab.Group> ) }

You can also access selected as an argument to a function passed to className if you only need to change the classes on the button:

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab
className={({ selected }) =>
selected ? 'bg-blue-500 text-white' : 'bg-white text-black'
}
>
Tab 1 </Tab> {/* ... */} </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> {/* ... */} </Tab.Panels> </Tab.Group> ) }

To disable a tab, use the disabled prop on the Tab component. Disabled tabs cannot be selected with the mouse, and are also skipped when navigating the tab list using the keyboard.

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group> <Tab.List> <Tab>Tab 1</Tab>
<Tab disabled>Tab 2</Tab>
<Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

By default, tabs are automatically selected as the user navigates through them using the arrow keys.

If you'd rather not change the current tab until the user presses Enter or Space, use the manual prop on the Tab.Group component. This can be helpful if selecting a tab performs an expensive operation and you don't want to run it unnecessarily.

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group manual>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

The manual prop has no impact on mouse interactions — tabs will still be selected as soon as they are clicked.

If you've styled your Tab.List to appear vertically, use the vertical prop to enable navigating with the up and down arrow keys instead of left and right, and to update the aria-orientation attribute for assistive technologies.

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group vertical>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

To change which tab is selected by default, use the defaultIndex={number} prop on the Tab.Group component.

import { Tab } from '@headlessui/react' function MyTabs() { return (
<Tab.Group defaultIndex={1}>
<Tab.List> <Tab>Tab 1</Tab>
{/* Selects this tab by default */}
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel>
{/* Displays this panel by default */}
<Tab.Panel>Content 2</Tab.Panel>
<Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

If you happen to provide an index that is out of bounds, then the last non-disabled tab will be selected on initial render. (For example, <Tab.Group defaultIndex={5} in the example above would render the third panel as selected.)

To run a function whenever the selected tab changes, use the onChange prop on the Tab.Group component.

import { Tab } from '@headlessui/react' function MyTabs() { return ( <Tab.Group
onChange={(index) => {
console.log('Changed selected tab to:', index)
}}
>
<Tab.List> <Tab>Tab 1</Tab> <Tab>Tab 2</Tab> <Tab>Tab 3</Tab> </Tab.List> <Tab.Panels> <Tab.Panel>Content 1</Tab.Panel> <Tab.Panel>Content 2</Tab.Panel> <Tab.Panel>Content 3</Tab.Panel> </Tab.Panels> </Tab.Group> ) }

Clicking a Tab will select that tab and display the corresponding Tab.Panel.

All interactions apply when a Tab component is focused.

CommandDescription

ArrowLeft and ArrowRight

Selects the previous/next non-disabled tab.

ArrowUp and ArrowDownwhen vertical is set

Selects the previous/next non-disabled tab.

Home or PageUp

Selects the first non-disabled tab.

End or PageDown

Selects the last non-disabled tab.

Enter or Space when manual is set

Activates the selected tab.

All relevant ARIA attributes are automatically managed.

For a full reference on all accessibility features implemented in Tabs, see the ARIA spec on Tabs.

The main Tab.Group component.

PropDefaultDescription
asFragment
String | Component

The element or component the Tabs should render as.

defaultIndex0
Number

The default selected index

onChange
(index: number) => void

A function called whenever the active tab changes.

verticalfalse
Boolean

When true, the orientation of the Tab.List will be vertical, otherwise it will be horizontal.

manualfalse
Boolean

When true, the user can only display a panel via the keyboard by first navigating to it using the arrow keys, and then by pressing Enter or Space. By default, panels are automatically displayed when navigated to via the arrow keys. Note that this prop has no affect on mouse behavior.

Render PropDescription
selectedIndex

Number

The currently selected index.

PropDefaultDescription
asdiv
String | Component

The element or component the Tab.List should render as.

Render PropDescription
selectedIndex

Number

The currently selected index.

PropDefaultDescription
asbutton
String | Component

The element or component the Tab should render as.

Render PropDescription
selected

Boolean

Whether or not the Tab is currently selected.

PropDefaultDescription
asdiv
String | Component

The element or component the Tab.Panels should render as.

Render PropDescription
selectedIndex

Number

The currently selected index.

PropDefaultDescription
asdiv
String | Component

The element or component the Tab.Panel should render as.

Render PropDescription
selected

Boolean

Whether or not the Tab.Panel is currently selected.

If you're interested in predesigned component examples using Headless UI and Tailwind CSS, check out Tailwind UI — a collection of beautifully designed and expertly crafted components built by us.

It's a great way to support our work on open-source projects like this and makes it possible for us to improve them and keep them well-maintained.