The alignment of the tabs
Tabs
A React component that helps you build accessible tabs, by providing keyboard interactions and ARIA attributes described in the WAI-ARIA Tab Panel Design Pattern.
Import#
import { Tabs, TabList, TabPanels, Tab, TabPanel } from '@chakra-ui/react'
- 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#
You can render any element within Tabs
, but TabList
should only have Tab
as children, and TabPanels
should have TabPanel
as children.
Tabs expects TabList
and TabPanels
as children. The order doesn't matter,
you can have TabList
at the top, at the bottom, or both.
<Tabs><TabList><Tab>One</Tab><Tab>Two</Tab><Tab>Three</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel><TabPanel><p>three!</p></TabPanel></TabPanels></Tabs>
Tab variants and color#
Tabs come in 6 different variants to style the tabs: line
,enclosed
,
enclosed-colored
, soft-rounded
, solid-rounded
<Tabs variant='enclosed'><TabList><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
You can also change the color scheme for any specific variant by passing the
colorScheme
.
The value of
colorScheme
must exist in the theme object, and must be a key intheme.colors
that has the50
-900
color values.
<Tabs variant='soft-rounded' colorScheme='green'><TabList><Tab>Tab 1</Tab><Tab>Tab 2</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Tab sizes#
You can change the size of the tab by passing size
prop. We support 3 sizes
sm
, md
, lg
<Tabs size='md' variant='enclosed'><TabList><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Changing the tabs alignment#
You can change the alignment of the TabList
by passing align
prop. We
support 3 sizes start
, center
, end
.
<Tabs align='end' variant='enclosed'><TabList><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Fitted Tabs#
Stretch the tab list to fit the container by passing isFitted
prop.
<Tabs isFitted variant='enclosed'><TabList mb='1em'><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Styling the tab states via props#
In the event that you need to create custom styles for the tabs, simply set the variant
to unstyled
, and use the _selected
, _hover
, _active
style props.
<Tabs variant='unstyled'><TabList><Tab _selected={{ color: 'white', bg: 'blue.500' }}>Tab 1</Tab><Tab _selected={{ color: 'white', bg: 'green.400' }}>Tab 2</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Tabs onChange#
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.
function Example() {const colors = useColorModeValue(['red.50', 'teal.50', 'blue.50'],['red.900', 'teal.900', 'blue.900'],)const [tabIndex, setTabIndex] = useState(0)const bg = colors[tabIndex]return (<Tabs onChange={(index) => setTabIndex(index)} bg={bg}><TabList><Tab>Red</Tab><Tab>Teal</Tab><Tab>Blue</Tab></TabList><TabPanels p='2rem'><TabPanel>The Primary Colors</TabPanel><TabPanel>Are 1, 2, 3</TabPanel><TabPanel>Red, yellow and blue.</TabPanel></TabPanels></Tabs>)}
Make a tab initially active#
If you want a tab to be initially active, simply pass the defaultIndex
prop
and set it to the index of that tab.
<Tabs defaultIndex={1}><TabPanels><TabPanel><ImageboxSize='200px'fit='cover'src='https://www.freepnglogos.com/uploads/naruto-png/image-kunai-naruto-fictional-battle-omniverse-23.png'/></TabPanel><TabPanel><ImageboxSize='200px'fit='cover'src='https://images5.alphacoders.com/810/thumbbig-810547.webp'/></TabPanel></TabPanels><TabList><Tab>Naruto</Tab><Tab>Sasuke</Tab></TabList></Tabs>
Make a Tab disabled#
When a Tab
is disabled, it is skipped during keyboard navigation and it is not
clickable.
function Example() {return (<Tabs><TabList><Tab>One</Tab><Tab isDisabled>Two</Tab><Tab>Three</Tab></TabList><TabPanels><TabPanel>1</TabPanel><TabPanel>2</TabPanel><TabPanel>3</TabPanel></TabPanels></Tabs>)}
Tabs with manual activation#
By default, Tabs
are activated automatically. This means when you use the
arrow keys to change tabs, the tab is activated and focused.
The content of a
TabPanel
should ideally be preloaded. However, if switching to a tab panel causes a network request and possibly a page refresh, there might be some noticeable latency and this might affect the experience for keyboard and screen reader users.
In this scenario, you should use a manually activated tab, it moves focus without activating the tabs. With focus on a specific tab, users can activate a tab by pressing Space or Enter.
<Tabs isManual variant='enclosed'><TabList><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels><TabPanel><p>one!</p></TabPanel><TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Lazily mounting tab panels#
By default, the Tabs
component renders all tabs content to the DOM, meaning
that invisible tabs are still rendered but are hidden by styles.
If you want to defer rendering of each tab until that tab is selected, you can
use the isLazy
prop. This is useful if your tabs require heavy performance, or
make network calls on mount that should only happen when the component is
displayed.
<Tabs isLazy><TabList><Tab>One</Tab><Tab>Two</Tab></TabList><TabPanels>{/* initially mounted */}<TabPanel><p>one!</p></TabPanel>{/* initially not mounted */}<TabPanel><p>two!</p></TabPanel></TabPanels></Tabs>
Controlled Tabs#
Like form inputs, a tab's state can be controlled. Make sure to include an onChange as well, or else the tabs will not be interactive.
function ControlledExample() {const [tabIndex, setTabIndex] = useState(0)const handleSliderChange = (event) => {setTabIndex(parseInt(event.target.value, 10))}const handleTabsChange = (index) => {setTabIndex(index)}return (<Box><inputtype='range'min='0'max='2'value={tabIndex}onChange={handleSliderChange}/><Tabs index={tabIndex} onChange={handleTabsChange}><TabList><Tab>One</Tab><Tab>Two</Tab><Tab>Three</Tab></TabList><TabPanels><TabPanel><p>Click the tabs or pull the slider around</p></TabPanel><TabPanel><p>Yeah yeah. What's up?</p></TabPanel><TabPanel><p>Oh, hello there.</p></TabPanel></TabPanels></Tabs></Box>)}
Creating custom tab components#
Because TabList
needs to know the order of the children, we use cloneElement
to pass state internally. Your custom Tab
component must use
React.forwardRef
.
function CustomTabs() {const CustomTab = React.forwardRef((props, ref) => {// 1. Reuse the `useTab` hookconst tabProps = useTab({ ...props, ref })const isSelected = !!tabProps['aria-selected']// 2. Hook into the Tabs `size`, `variant`, propsconst styles = useMultiStyleConfig('Tabs', tabProps)return (<Button __css={styles.tab} {...tabProps}><Box as='span' mr='2'>{isSelected ? '😎' : '😐'}</Box>{tabProps.children}</Button>)})return (<Tabs><TabList><CustomTab>One</CustomTab><CustomTab>Two</CustomTab></TabList><TabPanels><TabPanel>1</TabPanel><TabPanel>2</TabPanel></TabPanels></Tabs>)}
DataTabs#
If you'd like to drive your tabs with an array instead of using the granular components, you can create your own DataTabs component.
function Example() {// 1. Create the componentfunction DataTabs({ data }) {return (<Tabs><TabList>{data.map((tab, index) => (<Tab key={index}>{tab.label}</Tab>))}</TabList><TabPanels>{data.map((tab, index) => (<TabPanel p={4} key={index}>{tab.content}</TabPanel>))}</TabPanels></Tabs>)}// 2. Create an array of dataconst tabData = [{label: 'Nigerian Jollof',content: 'Perhaps the greatest dish ever invented.',},{label: 'Pounded Yam & Egusi',content:'Perhaps the surest dish ever invented but fills the stomach more than rice.',},]// 3. Pass the props and chill!return <DataTabs data={tabData} />}
Accessibility#
Keyboard#
Key | Action |
---|---|
ArrowLeft | Moves focus to the next tab |
ArrowRight | Moves focus to the previous tab |
Tab | When focus moves into the tab list, places focus on the active tab element |
Space or Enter | Activates the tab if it was not activated automatically on focus |
Home | Moves focus to the first tab |
End | Moves focus to the last tab |
ARIA roles#
Component | Aria | Usage |
---|---|---|
Tab | role="tab" | Indicates that it is a tab |
aria-selected | Set to true a tab is selected and all other Tabs have it set to false | |
aria-controls | Set to the id of its associated TabPanel | |
TabList | id | The id of the TabPanel that's referenced by its associated Tab |
aria-orientation | Set to vertical or horizontal based on the value of the orientation prop | |
role="tablist" | Indicates that it is a tablist | |
TabPanel | role="tabpanel" | Indicates that it is a tabpanel |
aria-labelledby | Set to the id of the Tab that labels the TabPanel |
Props#
Tabs Props#
Tabs composes Box
so you call pass all Box
related props.
align
align
"center" | "end" | "start"
colorScheme
colorScheme
"whiteAlpha" | "blackAlpha" | "gray" | "red" | "orange" | "yellow" | "green" | "teal" | "blue" | "cyan" | "purple" | "pink" | "linkedin" | "facebook" | "messenger" | "whatsapp" | "twitter" | "telegram"
"blue"
defaultIndex
defaultIndex
The initial index of the selected tab (in uncontrolled mode)
number
direction
direction
The writing mode direction. - When in RTL, the left and right navigation is flipped
"ltr" | "rtl"
"ltr"
id
id
The id of the tab
string
index
index
The index of the selected tab (in controlled mode)
number
isFitted
isFitted
If true
, tabs will stretch to width of the tablist.
boolean
isLazy
isLazy
Performance 🚀:
If true
, rendering of the tab panel's will be deferred until it is selected.
boolean
isManual
isManual
If true
, the tabs will be manually activated and
display its panel by pressing Space or Enter.
If false
, the tabs will be automatically activated
and their panel is displayed when they receive focus.
boolean
lazyBehavior
lazyBehavior
Performance 🚀: The lazy behavior of tab panels' content when not active. Only works when `isLazy={true}` - "unmount": The content of inactive tab panels are always unmounted. - "keepMounted": The content of inactive tab panels is initially unmounted, but stays mounted when selected.
LazyMode
"unmount"
onChange
onChange
Callback when the index (controlled or un-controlled) changes.
((index: number) => void)
orientation
orientation
The orientation of the tab list.
"horizontal" | "vertical"
"horizontal"
size
size
"sm" | "md" | "lg"
"md"
variant
variant
"line" | "enclosed" | "enclosed-colored" | "soft-rounded" | "solid-rounded" | "unstyled"
"line"
Tab Props#
id
id
string
isDisabled
isDisabled
If true
, the Tab
won't be toggleable
boolean
isSelected
isSelected
boolean
panelId
panelId
string
The Tabs
component is a multipart component. The styling needs to be applied
to each part specifically.
To learn more about styling multipart components, visit the Component Style page.
Anatomy#
- A:
root
- B:
tab
- C:
tablist
- D:
tabpanels
- E:
tabpanel
Theming properties#
The properties that affect the theming of the Tabs
component are:
variant
: The visual variant of the component. Defaults toline
.size
: The size of the component. Defaults tomd
.colorScheme
: The color scheme of the component. Defaults toblue
.
Theming utilities#
createMultiStyleConfigHelpers
: a function that returns a set of utilities for creating style configs for a multipart component (definePartsStyle
anddefineMultiStyleConfig
).definePartsStyle
: a function used to create multipart style objects.defineMultiStyleConfig
: a function used to define the style configuration for a multipart component.
import { tabsAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers, defineStyle } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(tabsAnatomy.keys)
Customizing the default theme#
import { tabsAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/react'const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(tabsAnatomy.keys)// define the base component stylesconst baseStyle = definePartsStyle({// define the part you're going to styletab: {fontWeight: 'semibold', // change the font weight},tabpanel: {fontFamily: 'mono', // change the font family},})// export the component themeexport const tabsTheme = defineMultiStyleConfig({ baseStyle })
After customizing the the Tabs
theme, we can import it in our theme file and
add it in the components
property.
import { extendTheme } from '@chakra-ui/react'import { tabsTheme } from './components/Tabs'const theme = extendTheme({components: {Tabs: tabsTheme,},})export default theme
Adding a custom size#
Let's assume we want to include an extra large tab size. Here's how we can do that:
import { tabsAnatomy } from '@chakra-ui/anatomy';import { createMultiStyleConfigHelpers } from '@chakra-ui/react';const { definePartsStyle, defineMultiStyleConfig } =createMultiStyleConfigHelpers(tabsAnatomy.keys);// define custom sizesconst sizes = {xl: definePartsStyle({// define the parts that will change for each sizetab: {fontSize: 'xl',py: '4',px: '6',},tabpanel: {py: '4',px: '6',},}),};// export the component themeexport const tabsTheme = defineMultiStyleConfig({ sizes });// now we can use the new `xl` size<Tabs size="xl" ... />
Every time you're adding anything new to the theme, you'd need to run the CLI command to get proper autocomplete in your IDE. You can learn more about the CLI tool here.
Adding a custom variant#
Let's assume we want to include a custom variant that is fully enclosed and can have a color scheme applied. Here's how we can do that:
import { tabsAnatomy } from '@chakra-ui/anatomy'import { createMultiStyleConfigHelpers } from '@chakra-ui/react'import { mode } from '@chakra-ui/theme-tools' // import utility to set light and dark mode props// define a custom variantconst colorfulVariant = definePartsStyle((props) => {const { colorScheme: c } = props // extract colorScheme from component propsreturn {tab: {border: '2px solid',borderColor: 'transparent',// use colorScheme to change background color with dark and light mode optionsbg: mode(`${c}.300`, `${c}.600`)(props),borderTopRadius: 'lg',borderBottom: 'none',_selected: {bg: mode('#fff', 'gray.800')(props),color: mode(`${c}.500`, `${c}.300`)(props),borderColor: 'inherit',borderBottom: 'none',mb: '-2px',},},tablist: {borderBottom: '2x solid',borderColor: 'inherit',},tabpanel: {border: '2px solid',borderColor: 'inherit',borderBottomRadius: 'lg',borderTopRightRadius: 'lg',},}})const variants = {colorful: colorfulVariant,}// export the component themeexport const tabsTheme = defineMultiStyleConfig({ variants })// now we can use the `colorful` variant with a different color Scheme<Tabs variant="colorful" colorScheme="purple" ... />
Changing the default properties#
Let's assume we cant to change the default size, variant, and color scheme of every tab in our app. Here's how we can do that:
import { tabsAnatomy } from '@chakra-ui/anatomy';import { createMultiStyleConfigHelpers } from '@chakra-ui/react';// define which sizes, variants, and color schemes are applied by defaultconst defaultProps = {size: 'xl',variant: 'colorful',colorScheme: 'green',};// export the component themeexport const tabsTheme = defineMultiStyleConfig({ defaultProps })// This saves you time, instead of manually setting the// size and variant every time you use a tabs component:<Tabs size="xl" variant="colorful" colorScheme="green" ... />
Showcase#
import React from 'react'; import { ChakraProvider, Box, SimpleGrid, GridItem, Tabs, TabList, TabPanels, Tab, TabPanel, } from '@chakra-ui/react'; import theme from './theme'; import { ColorModeSwitcher } from './ColorModeSwitcher'; export default function App() { return ( <ChakraProvider theme={theme}> <Box position="relative" h="100vh" p={12}> <SimpleGrid columns={[1, 1, 1, 2]} spacing={12}> <GridItem> <Tabs> <TabList> <Tab>One</Tab> <Tab>Two</Tab> <Tab>Three</Tab> <Tab isDisabled>Disabled</Tab> </TabList> <TabPanels> <TabPanel> <p>New default appearance defined by theme</p> </TabPanel> <TabPanel> <p>Tab panel two</p> </TabPanel> <TabPanel> <p>Tab panel three</p> </TabPanel> </TabPanels> </Tabs> </GridItem> <GridItem> <Tabs size="lg" colorScheme="purple"> <TabList> <Tab>One</Tab> <Tab>Two</Tab> <Tab>Three</Tab> <Tab isDisabled>Disabled</Tab> </TabList> <TabPanels> <TabPanel> <p>Large size with purple color scheme</p> </TabPanel> <TabPanel> <p>Tab panel two</p> </TabPanel> <TabPanel> <p>Tab panel three</p> </TabPanel> </TabPanels> </Tabs> </GridItem> <GridItem> <Tabs size="md" colorScheme="cyan"> <TabList> <Tab>One</Tab> <Tab>Two</Tab> <Tab>Three</Tab> <Tab isDisabled>Disabled</Tab> </TabList> <TabPanels> <TabPanel> <p>Medium size with cyan color scheme</p> </TabPanel> <TabPanel> <p>Tab panel two</p> </TabPanel> <TabPanel> <p>Tab panel three</p> </TabPanel> </TabPanels> </Tabs> </GridItem> <GridItem> <Tabs size="sm" colorScheme="orange"> <TabList> <Tab>One</Tab> <Tab>Two</Tab> <Tab>Three</Tab> <Tab isDisabled>Disabled</Tab> </TabList> <TabPanels> <TabPanel> <p>Small size with orange color scheme</p> </TabPanel> <TabPanel> <p>Tab panel two</p> </TabPanel> <TabPanel> <p>Tab panel three</p> </TabPanel> </TabPanels> </Tabs> </GridItem> </SimpleGrid> <ColorModeSwitcher /> </Box> </ChakraProvider> ); }