code/tamagui.dev/data/docs/components/tabs/2.0.0.mdx
<Tabs.Content value="styled">
</Tabs.Content> <Tabs.Content value="unstyled">
</Tabs.Content> <Tabs.Content value="headless">
</Tabs.Content>
<Highlights
features={[
Accessible, easy to compose, customize and animate,
Sizable & works controlled or uncontrolled,
Supports automatic and manual activation modes,
Full keyboard navigation,
]}
/>
<Tabs.Content value="styled">
Tabs is already installed in tamagui, or you can install it independently:
npm install @tamagui/tabs
</Tabs.Content>
<Tabs.Content value="unstyled">
Tabs is already installed in tamagui, or you can install it independently:
npm install @tamagui/tabs
</Tabs.Content>
<Tabs.Content value="headless">
To use the headless tabs, import from the @tamagui/tabs-headless package. This package has no dependency on @tamagui/core and provides hooks for building custom tab interfaces with any styling solution.
npm install @tamagui/tabs-headless
</Tabs.Content>
<Tabs.Content value="styled">
import { SizableText, Tabs } from 'tamagui'
export default () => (
<Tabs defaultValue="tab1" width={400}>
<Tabs.List>
<Tabs.Tab value="tab1">
<SizableText>Tab 1</SizableText>
</Tabs.Tab>
<Tabs.Tab value="tab2">
<SizableText>Tab 2</SizableText>
</Tabs.Tab>
</Tabs.List>
<Tabs.Content value="tab1">
<H5>Tab 1</H5>
</Tabs.Content>
<Tabs.Content value="tab2">
<H5>Tab 2</H5>
</Tabs.Content>
</Tabs>
)
</Tabs.Content>
<Tabs.Content value="unstyled">
Use the createTabs export to create fully custom tabs that still use the Tamagui styling system. You provide your own styled frame components and get back a fully functional tabs component.
</Tabs.Content>
<Tabs.Content value="headless">
The useTabs hook provides all the state and accessibility props needed to build custom tabs with any styling solution.
import { useTabs } from '@tamagui/tabs-headless'
function MyTabs() {
const { tabsProps, listProps, getTabProps, getContentProps, value } = useTabs({
defaultValue: 'tab1',
orientation: 'horizontal',
})
return (
<div {...tabsProps}>
<div {...listProps}>
<button {...getTabProps('tab1')}>Tab 1</button>
<button {...getTabProps('tab2')}>Tab 2</button>
</div>
<div {...getContentProps('tab1')}>{value === 'tab1' && <p>Content 1</p>}</div>
<div {...getContentProps('tab2')}>{value === 'tab2' && <p>Content 2</p>}</div>
</div>
)
}
For more complex use cases, you can use the context-based hooks:
import { useTabs, TabsProvider, useTab, useTabContent } from '@tamagui/tabs-headless'
function Tabs({ children, ...props }) {
const { contextValue, tabsProps } = useTabs(props)
return (
<TabsProvider value={contextValue}>
<div {...tabsProps}>{children}</div>
</TabsProvider>
)
}
function Tab({ value, children }) {
const { isSelected, tabProps } = useTab({ value })
return (
<button {...tabProps} style={{ fontWeight: isSelected ? 'bold' : 'normal' }}>
{children}
</button>
)
}
function TabContent({ value, children }) {
const { shouldMount, contentProps } = useTabContent({ value })
if (!shouldMount) return null
return <div {...contentProps}>{children}</div>
}
</Tabs.Content>
<Tabs.Content value="styled">
Root tabs component. Extends Stack. Passing the size prop to this component will affect the descendants.
</Tabs.Content> <Tabs.Content value="unstyled">
When using createTabs, you provide three styled components:
TabsFrame: The root container componentTabFrame: The tab trigger componentContentFrame: The content container component</Tabs.Content> <Tabs.Content value="headless">
The useTabs hook accepts these options and returns props/helpers:
</Tabs.Content>
<PropsTable
data={[
{
name: 'value',
type: 'string',
description: The value for the selected tab, if controlled.,
},
{
name: 'defaultValue',
type: 'string',
description: The value of the tab to select by default, if uncontrolled.,
},
{
name: 'onValueChange',
type: '(value: string) => void',
description: A function called when a new tab is selected.,
},
{
name: 'orientation',
type: '"horizontal" | "vertical"',
default: 'horizontal',
description: The orientation the tabs are laid out in.,
},
{
name: 'dir',
type: '"ltr" | "rtl"',
description: The direction of navigation between toolbar items.,
},
{
name: 'activationMode',
type: '"manual" | "automatic"',
default: 'manual',
description: Whether a tab is activated automatically (on focus) or manually (on click/enter).,
},
{
name: 'loop',
type: 'boolean',
default: 'true',
description: Whether keyboard navigation should loop from last to first and vice versa.,
},
]}
/>
<Tabs.Content value="headless">
| Property | Type | Description |
|---|---|---|
value | string | The currently selected tab value |
setValue | (value: string) => void | Function to change the selected tab |
direction | 'ltr' | 'rtl' | The resolved text direction |
tabsProps | object | Props to spread on the tabs container |
listProps | object | Props to spread on the tab list |
getTabProps | (value: string, disabled?: boolean) => object | Get props for a tab trigger |
getContentProps | (value: string) => object | Get props for a tab content panel |
contextValue | TabsContextValue | Context value for component-based API |
Hook for individual tab triggers when using the component-based API.
const { isSelected, tabProps } = useTab({
value: 'tab1',
disabled: false,
onPress: () => {},
onKeyDown: () => {},
onFocus: () => {},
})
Hook for tab content panels when using the component-based API.
const { isSelected, shouldMount, contentProps } = useTabContent({
value: 'tab1',
forceMount: false,
})
</Tabs.Content>
<Tabs.Content value="styled">
Container for the trigger buttons. Supports scrolling by extending Group. You can disable passing border radius to children by passing disablePassBorderRadius.
<PropsTable
data={[
{
name: 'loop',
type: 'boolean',
default: 'true',
description: Whether or not to loop over after reaching the end or start of the items. Used mainly for managing keyboard navigation.,
},
]}
/>
Extends ThemeableStack, adding:
<PropsTable
data={[
{
name: 'value',
type: 'string',
description: The value for the tabs state to be changed to after activation of the trigger.,
},
{
name: 'onInteraction',
type: (type: InteractionType, layout: TabLayout | null) => void,
description: Used for making custom indicators when trigger is interacted with.,
},
{
name: 'disabled',
type: boolean,
description: Whether the tab is disabled.,
},
{
name: 'unstyled',
type: boolean,
description: When true, removes all default Tamagui styling.,
},
{
name: 'activeStyle',
type: StyleProp,
description: Styles to apply when the tab is selected.,
},
{
name: 'activeTheme',
type: string | null,
description: Theme to apply when the tab is selected. Set to null for no theme change.,
},
]}
/>
Where each tab's content will be shown. Extends ThemeableStack, adding:
<PropsTable
data={[
{
name: 'value',
type: 'string',
description: Will show the content when the value matches the state of Tabs root.,
},
{
name: 'forceMount',
type: 'boolean',
default: 'false',
description: Used to force mounting when more control is needed. Useful when controlling animation with Tamagui animations.,
},
]}
/>
Here is a demo with more advanced animations using AnimatePresence and Tab's onInteraction prop.
</Tabs.Content>
</Tabs>