Tabs
Tabs organize content into multiple, accessible sections.
import {QTabs, QTabList, QTab, QTabPanels, QTabPanel} from "@qui/react"
Overview
Tabs
: Provides context and state for all components.TabList
: Wrapper for the Tab components.Tab
: element that serves as a label for one of the tab panels and can be activated to display that panel.TabPanels
: Wrapper for the TabPanel components.TabPanel
: element that contains the content associated with a tab.
Usage Guidelines:
- The number of tabs should be limited to a number that is easy for the user to remember and navigate. Too many tabs can be overwhelming for the user, while too few tabs may not provide enough flexibility.
- Tab labels should be concise, and they should accurately reflect the content of the corresponding section.
- Tabs should be ordered in a way that makes sense for the user.
Accessibility
Our implementation is based on the ARIA Tabs pattern.
Examples
Showcase
Profile Panel
Preferences Panel
Disabled Panel
Files Panel
import {ReactNode} from "react"import {Files, Settings, Settings2, User} from "lucide-react"import {QTab, QTabList, QTabPanel, QTabPanels, QTabs} from "@qui/react"export default function Showcase(): ReactNode {return (<QTabs><QTabList><QTab startIcon={User}>Profile</QTab><QTab startIcon={Settings2}>Preferences</QTab><QTab disabled startIcon={Settings}>Disabled</QTab><QTab startIcon={Files}>Files</QTab></QTabList><QTabPanels><QTabPanel>Profile Panel</QTabPanel><QTabPanel>Preferences Panel</QTabPanel><QTabPanel>Disabled Panel</QTabPanel><QTabPanel>Files Panel</QTabPanel></QTabPanels></QTabs>)}
Manual Activation
It is recommended that tabs activate automatically when they receive focus as long as their associated tab panels are displayed without noticeable latency. This is the default behavior. Set manual to true
to disable this functionality. When true
, tabs will not automatically open when focused, and will require either a click or key event (Space or Enter).
Profile Panel
Preferences Panel
Disabled Panel
Files Panel
import {ReactNode} from "react"import {Files, Settings, Settings2, User} from "lucide-react"import {QTab, QTabList, QTabPanel, QTabPanels, QTabs} from "@qui/react"export default function ManualActivation(): ReactNode {return (<QTabs manual><QTabList><QTab startIcon={User}>Profile</QTab><QTab startIcon={Settings2}>Preferences</QTab><QTab disabled startIcon={Settings}>Disabled</QTab><QTab startIcon={Files}>Files</QTab></QTabList><QTabPanels><QTabPanel>Profile Panel</QTabPanel><QTabPanel>Preferences Panel</QTabPanel><QTabPanel>Disabled Panel</QTabPanel><QTabPanel>Files Panel</QTabPanel></QTabPanels></QTabs>)}
Vertical Tabs
Set the orientation prop to vertical
to orient the tabs from top to bottom instead of left to right. In vertical mode, the active tab panel displays to the right or left of the tablist, depending on the value of alignment.
Profile Panel
Preferences Panel
Files Panel
Profile Panel
Preferences Panel
Files Panel
import {ReactNode} from "react"import {Files, Settings2, User} from "lucide-react"import {QTab, QTabList, QTabPanel, QTabPanels, QTabs} from "@qui/react"export default function Showcase(): ReactNode {const tabContent = (<><QTabList><QTab startIcon={User}>Profile</QTab><QTab startIcon={Settings2}>Preferences</QTab><QTab startIcon={Files}>Files</QTab></QTabList><QTabPanels><QTabPanel>Profile Panel</QTabPanel><QTabPanel>Preferences Panel</QTabPanel><QTabPanel>Files Panel</QTabPanel></QTabPanels></>)return (<div className="flex w-80 flex-col gap-8"><div className="q-border-default flex w-full rounded border p-4"><QTabs fullWidth orientation="vertical">{tabContent}</QTabs></div><div className="q-border-default flex w-full rounded border p-4"><QTabs alignment="end" fullWidth orientation="vertical">{tabContent}</QTabs></div></div>)}
Controlled
The onChange callback returns the active tab's index whenever the user changes tabs. If you intend to control the tabs programmatically, use this with the index prop.
import {ReactNode, useState} from "react"import {ChevronLeft, ChevronRight} from "lucide-react"import {QButton, QTab, QTabList, QTabPanel, QTabPanels, QTabs} from "@qui/react"export default function Controlled(): ReactNode {const [index, setIndex] = useState(0)const next = () => setIndex((prevIndex) => Math.min(2, prevIndex + 1))const prev = () => setIndex((prevIndex) => Math.max(0, prevIndex - 1))return (<QTabs accent="secondary" index={index} onChange={setIndex}><QTabList><QTab>One</QTab><QTab>Two</QTab><QTab>Three</QTab></QTabList><QTabPanels><QTabPanel className="flex gap-2"><QButton disabled endIcon={ChevronLeft} variant="outline">Prev</QButton><QButton endIcon={ChevronRight} onClick={next} variant="outline">Next</QButton></QTabPanel><QTabPanel className="flex gap-2"><QButton endIcon={ChevronLeft} onClick={prev} variant="outline">Prev</QButton><QButton endIcon={ChevronRight} onClick={next} variant="outline">Next</QButton></QTabPanel><QTabPanel className="flex gap-2"><QButton endIcon={ChevronLeft} onClick={prev} variant="outline">Prev</QButton><QButton disabled endIcon={ChevronRight} variant="outline">Next</QButton></QTabPanel></QTabPanels></QTabs>)}
Dismissable Tabs
Use the dismissable prop to render a close icon button. When clicked, the onTabDismissed callback will be called with the index and props of the dismissed tab, as well as the event that triggered the dismissal. Use this information to re-compute the visible tabs:
Profile Panel
Preferences Panel
Disabled Panel
Files Panel
import {ReactNode, useState} from "react"import {Files, Settings, Settings2, User} from "lucide-react"import {QTab,QTabDismissEvent,QTabList,QTabPanel,QTabPanelProps,QTabPanels,QTabProps,QTabs,} from "@qui/react"interface TabAndPanel {id: stringpanel: QTabPanelPropstab: QTabProps}export default function Dismissable(): ReactNode {const [tabConfig, setTabConfig] = useState<TabAndPanel[]>([{id: "profile",panel: {children: "Profile Panel",},tab: {children: "Profile",startIcon: User,},},{id: "preferences",panel: {children: "Preferences Panel",},tab: {children: "Preferences",startIcon: Settings2,},},{id: "disabled",panel: {children: "Disabled Panel",},tab: {children: "Disabled",disabled: true,startIcon: Settings,},},{id: "files",panel: {children: "Files Panel",},tab: {children: "Files",startIcon: Files,},},])const handleDismiss: QTabDismissEvent = (index) => {setTabConfig((prevState) => [...prevState.slice(0, index),...prevState.slice(index + 1),])}return (<QTabs dismissable onTabDismissed={handleDismiss}><QTabList>{tabConfig.map((entry) => {return (<QTab key={entry.id} {...entry.tab}>{entry.tab.children}</QTab>)})}</QTabList><QTabPanels>{tabConfig.map((entry) => {return (<QTabPanel key={entry.id} {...entry.panel}>{entry.panel.children}</QTabPanel>)})}</QTabPanels></QTabs>)}
Playground
Profile Panel
Preferences Panel
Files Panel
API
Props
Name & Description | Option(s) | Default |
---|---|---|
ReactNode | ||
The style of the tab buttons. |
| 'primary' |
Governs the horizontal placement of the tablist relative to the panel
content. |
| 'start' |
When true , the tab indicator bar will animate into position whenever a
tab is selected. |
| true |
The component used for the root node. It can be a React component or
element. |
| 'div' |
The className property of the Element
interface gets and sets the value of the
class attribute
of the specified element. |
| |
Controlled closed tabs. Each entry maps to a tab's numeric index. Closed
tabs are not visible. |
| |
The default closed tabs. Each entry maps to a tab's numeric index. |
| [] |
The initial index of the selected tab. |
| |
Render a close icon that calls onTabDismissed on click. |
| false |
If true , the tab container will take up the full height of its parent
container |
| false |
If true , the tab container will take up the full width of its parent
container |
| false |
The index of the selected tab (in controlled mode) |
| |
The lazy behavior of tab panel content when not active. |
| 'off' |
If true , each tab will be manually activated and display its panel by
pressing Space or Enter.If false , each tab will be automatically activated and their panel is
displayed when they receive focus. |
| false |
Callback fired when the index (controlled or un-controlled) changes. The callback includes a reason parameter which indicates why the event
was fired: |
| |
Callback fired when a tab's close icon is clicked. |
| |
The orientation of the tab list. |
| 'horizontal' |
The size of the tab buttons and their icons. |
| 'm' |
The style global attribute
contains CSS styling
declarations to be applied to the element. | CSSProperties |
Type
| 'primary'| 'secondary'
Default
'primary'
Description
The style of the tab buttons.
Type
| 'start'| 'end'
Default
'start'
Description
Governs the horizontal placement of the tablist relative to the panel
content.
Type
boolean
Default
true
Description
When
true
, the tab indicator bar will animate into position whenever a
tab is selected.Type
| ElementType| ComponentType
Default
'div'
Description
The component used for the root node. It can be a React component or
element.
Type
string
Description
The className property of the Element
interface gets and sets the value of the
class attribute
of the specified element.
Type
number[]
Description
Controlled closed tabs. Each entry maps to a tab's numeric index. Closed
tabs are not visible.
Related symbols
Type
number[]
Default
[]
Description
The default closed tabs. Each entry maps to a tab's numeric index.
Type
number
Description
The initial index of the selected tab.
Type
boolean
Default
false
Description
If
true
, the tab container will take up the full height of its parent
containerType
boolean
Default
false
Description
If
true
, the tab container will take up the full width of its parent
containerType
number
Description
The index of the selected tab (in controlled mode)
Type
| 'unmount'| 'keepMounted'| 'off'
Default
'off'
Description
The lazy behavior of tab panel content when not active.
Type
boolean
Default
false
Description
If
If
true
, each tab will be manually activated and display its panel by
pressing Space or Enter.If
false
, each tab will be automatically activated and their panel is
displayed when they receive focus.Type
(index: number,reason: 'select' | 'reset',event?: SyntheticEvent,) => void
Description
Callback fired when the index (controlled or un-controlled) changes.
The callback includes a
The callback includes a
reason
parameter which indicates why the event
was fired:Type
(index: number,props: QTabProps,event: SyntheticEvent,) => void
Description
Callback fired when a tab's close icon is clicked.
Type
| 'horizontal'| 'vertical'
Default
'horizontal'
Description
The orientation of the tab list.
Type
| 's'| 'm'| 'l'
Default
'm'
Description
The size of the tab buttons and their icons.
Type
CSSProperties
Description
The style global attribute
contains CSS styling
declarations to be applied to the element.
Keyboard Events
When focus moves into the tab list, places focus on the active tab element.
When the tab list contains the focus, moves focus to the next element in the page tab sequence outside the tablist, which is the tabpanel.
If the tablist is focused and orientation is horizontal
:
moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. If manual is
false
, activates the newly focused tab. Also see Manual Activation.
moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab. If manual is
false
, activates the newly focused tab. Also see Manual Activation.
If the tablist is focused and orientation is vertical
:
- performs as Right Arrow described above.
- performs as Left Arrow described above.
When focus is on a tab in the tablist with either horizontal or vertical orientation:
Moves focus to the first available tab. If manual is false
, activates
the newly focused tab. Also see Manual
Activation.
Moves focus to the last available tab. If manual is false
,
activates the newly focused tab. Also see Manual
Activation.
If dismissable is
true
, deletes (closes) the current tab element and its associated tab panel, sets focus on the tab following the tab that was closed, and optionally activates the newly focused tab.If there is not a tab that followed the tab that was deleted, e.g., the deleted tab was the right-most tab in a left-to-right horizontal tab list, sets focus on and optionally activates the tab that preceded the deleted tab.
If the application allows all tabs to be deleted, and the user deletes the last remaining tab in the tab list, the application moves focus to another element that provides a logical work flow.