Import
import { Tabs } from '@contentful/f36-components';
import { Tabs } from '@contentful/f36-tabs';
Examples
Basic
function TabsBasicExample() {
return (
<Tabs>
<Tabs.List>
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">Content for the first tab</Tabs.Panel>
<Tabs.Panel id="second">Content for the second tab</Tabs.Panel>
<Tabs.Panel id="third">Content for the third tab</Tabs.Panel>
</Tabs>
);
}
With divider
By default, Tabs
has no divider, but you can add it by providing variant
prop to the Tabs.List
With vertical divider:
function TabsWithVerticalDividerExample() {
return (
<Tabs>
<Tabs.List variant="vertical-divider">
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">Content for the first tab</Tabs.Panel>
<Tabs.Panel id="second">Content for the second tab</Tabs.Panel>
<Tabs.Panel id="third">Content for the third tab</Tabs.Panel>
</Tabs>
);
}
With horizontal divider:
function TabsWithHorizontalDividerExample() {
return (
<Tabs>
<Tabs.List variant="horizontal-divider">
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">Content for the first tab</Tabs.Panel>
<Tabs.Panel id="second">Content for the second tab</Tabs.Panel>
<Tabs.Panel id="third">Content for the third tab</Tabs.Panel>
</Tabs>
);
}
With default open tab
function TabsWithDefaultTabExample() {
return (
<Tabs defaultTab="first">
<Tabs.List>
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">Content for the first tab</Tabs.Panel>
<Tabs.Panel id="second">Content for the second tab</Tabs.Panel>
<Tabs.Panel id="third">Content for the third tab</Tabs.Panel>
</Tabs>
);
}
With disabled tab
function TabsWithDefaultTabExample() {
return (
<Tabs>
<Tabs.List>
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third" isDisabled>
Third (disabled)
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">Content for the first tab</Tabs.Panel>
<Tabs.Panel id="second">Content for the second tab</Tabs.Panel>
<Tabs.Panel id="third">Content for the third tab</Tabs.Panel>
</Tabs>
);
}
Controlled Tabs
By default, Tabs
is an uncontrolled component, but you can make it controlled by providing currentTab
and onTabChange
props.
function TabsControlledExample() {
const [currentTab, setCurrentTab] = useState('first');
return (
<Tabs currentTab={currentTab} onTabChange={setCurrentTab}>
<Tabs.List>
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="first">content first tab</Tabs.Panel>
<Tabs.Panel id="second">content second tab</Tabs.Panel>
<Tabs.Panel id="third">content third tab</Tabs.Panel>
</Tabs>
);
}
Controlled Tabs with always mounted Tabs.Panel
By default, Tabs.Panel
is unmounted, if it's not an active tab. But you can make it always mounted by passing the forceMount
prop.
Keep in mind that you have to provide additional styles to hide not active Tabs.Panel
.
function TabsControlledWithMountedContentExample() {
const [currentTab, setCurrentTab] = useState('first');
const getTabPanelStyles = (isVisible) =>
css({
display: isVisible ? 'block' : 'none',
});
return (
<Tabs currentTab={currentTab} onTabChange={setCurrentTab}>
<Tabs.List>
<Tabs.Tab panelId="first">First</Tabs.Tab>
<Tabs.Tab panelId="second">Second</Tabs.Tab>
<Tabs.Tab panelId="third">Third</Tabs.Tab>
</Tabs.List>
<Tabs.Panel
id="first"
forceMount
className={getTabPanelStyles(currentTab === 'first')}
>
content first tab
</Tabs.Panel>
<Tabs.Panel
id="second"
forceMount
className={getTabPanelStyles(currentTab === 'second')}
>
content second tab
</Tabs.Panel>
<Tabs.Panel
id="third"
forceMount
className={getTabPanelStyles(currentTab === 'third')}
>
content third tab
</Tabs.Panel>
</Tabs>
);
}
Tabs
Name | Type | Default |
---|
children | ReactNode | |
className | string CSS class to be appended to the root element | |
currentTab | string | |
defaultTab | string | |
onTabChange | (tab: string) => void | |
testId | string A [data-test-id] attribute used for testing purposes | |
Tab
Name | Type | Default |
---|
children required | ReactNode | |
panelId required | string A unique id that associates the tab with a panel. | |
className | string CSS class to be appended to the root element | |
isDisabled | false true When true, prevents the user from interacting with the tab. | |
testId | string A [data-test-id] attribute used for testing purposes | |
TabPanel
Name | Type | Default |
---|
children required | ReactNode | |
id required | string | |
className | string CSS class to be appended to the root element | |
forceMount | true Used to force mounting when more control is needed. Useful when
controlling animation with React animation libraries. | |
testId | string A [data-test-id] attribute used for testing purposes | |
TabList
Name | Type | Default |
---|
children | ReactNode | |
className | string CSS class to be appended to the root element | |
loop | false true When true, keyboard navigation will loop from last tab to first, and vice versa. | true |
testId | string A [data-test-id] attribute used for testing purposes | |
variant | "default" "horizontal-divider" "vertical-divider" visual variant of TabList | |
Content guidelines
- every
Tab
should have concise copy - all content within a Tab should be related to it's label
- to ensure an optimal cognitive load for users, do not use more than six (6) Tabs at any given time