Tabs
Use in pages to manage sub-pages
`
`
`
- Accessible, easy to compose, customize and animate
- Sizable & works controlled or uncontrolled
- Supports automatic and manual activation modes
- Full keyboard navigation
Installation
Tabs is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/tabsTabs is already installed in @hanzo/gui, or you can install it independently:
npm install @hanzogui/tabsTo use the headless tabs, import from the @hanzogui/tabs-headless package. This package has no dependency on @hanzogui/core and provides hooks for building custom tab interfaces with any styling solution.
npm install @hanzogui/tabs-headlessUsage
import { SizableText, Tabs } from '@hanzo/gui'
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>
)Use the createTabs export to create fully custom tabs that still use the Hanzo GUI styling system. You provide your own styled frame components and get back a fully functional tabs component.
The useTabs hook provides all the state and accessibility props needed to build custom tabs with any styling solution.
import { useTabs } from '@hanzogui/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>
)
}Component-Based API
For more complex use cases, you can use the context-based hooks:
import { useTabs, TabsProvider, useTab, useTabContent } from '@hanzogui/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>
}API Reference
Tabs
Root tabs component. Extends Stack. Passing the size prop to this component will affect the descendants.
When using createTabs, you provide three styled components:
TabsFrame: The root container componentTabFrame: The tab trigger componentContentFrame: The content container component
The useTabs hook accepts these options and returns props/helpers:
| Prop | Type | Default | Required |
|---|---|---|---|
| value | string | - | - |
| defaultValue | string | - | - |
| onValueChange | (value: string) => void | - | - |
| orientation | "horizontal" | "vertical" | horizontal | - |
| dir | "ltr" | "rtl" | - | - |
| activationMode | "manual" | "automatic" | manual | - |
| loop | boolean | true | - |
useTabs Return Value
| 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 |
useTab
Hook for individual tab triggers when using the component-based API.
const { isSelected, tabProps } = useTab({
value: 'tab1',
disabled: false,
onPress: () => {},
onKeyDown: () => {},
onFocus: () => {},
})useTabContent
Hook for tab content panels when using the component-based API.
const { isSelected, shouldMount, contentProps } = useTabContent({
value: 'tab1',
forceMount: false,
})Tabs.List
Container for the trigger buttons. Supports scrolling by extending Group. You can disable passing border radius to children by passing disablePassBorderRadius.
| Prop | Type | Default | Required |
|---|---|---|---|
| loop | boolean | true | - |
Since Tabs.List extends Group, the same limitation applies: automatic border radius
detection only works when Tabs.Tab is a direct child of Tabs.List. If you wrap
tabs in custom components, see the Group docs on nested
items for workarounds.
Tabs.Tab
Extends ThemeableStack, adding:
| Prop | Type | Default | Required |
|---|---|---|---|
| value | string | - | - |
| onInteraction | (type: InteractionType, layout: TabLayout | null) => void | - | - |
| disabled | boolean | - | - |
| unstyled | boolean | - | - |
| activeStyle | StyleProp | - | - |
| activeTheme | string | null | - | - |
Tabs.Content
Where each tab's content will be shown. Extends ThemeableStack, adding:
| Prop | Type | Default | Required |
|---|---|---|---|
| value | string | - | - |
| forceMount | boolean | false | - |
Examples
Animations
Here is a demo with more advanced animations using AnimatePresence and Tab's onInteraction prop.
Last updated on