Back to Tamagui

Tabs

code/tamagui.dev/data/docs/components/tabs/2.0.0.mdx

1.144.410.4 KB
Original Source
<YStack className="is-sticky" /> <Tabs id="type" defaultValue="styled"> <Tabs.List> <TooltipSimple label="With Tamagui's default styles"> <Tabs.Tab value="styled">Styled</Tabs.Tab> </TooltipSimple> <TooltipSimple label="No default styles, easy to customize"> <Tabs.Tab value="unstyled">Unstyled</Tabs.Tab> </TooltipSimple> <TooltipSimple label="No dependency on Tamagui's core"> <Tabs.Tab value="headless" label="No styles and no dependency on Tamagui's styling">Headless</Tabs.Tab> </TooltipSimple> </Tabs.List> <HeroContainer> <Tabs.Content value="styled" justifyContent="center" alignItems="center" width="100%"> <TabsDemo /> </Tabs.Content> <Tabs.Content value="unstyled" justifyContent="center" alignItems="center" width="100%"> <TabsHeadlessDemo /> </Tabs.Content> <Tabs.Content value="headless" justifyContent="center" alignItems="center" width="100%"> <TabsHeadlessDemo /> </Tabs.Content> </HeroContainer>

<Tabs.Content value="styled">

tsx

</Tabs.Content> <Tabs.Content value="unstyled">

tsx

</Tabs.Content> <Tabs.Content value="headless">

tsx

</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, ]} />

Installation

<Tabs.Content value="styled"> Tabs is already installed in tamagui, or you can install it independently:

bash
npm install @tamagui/tabs

</Tabs.Content>

<Tabs.Content value="unstyled"> Tabs is already installed in tamagui, or you can install it independently:

bash
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.

bash
npm install @tamagui/tabs-headless

</Tabs.Content>

Usage

<Tabs.Content value="styled">

tsx
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.

tsx

</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.

tsx
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>
  )
}

Component-Based API

For more complex use cases, you can use the context-based hooks:

tsx
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>

API Reference

Tabs

<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 component
  • TabFrame: The tab trigger component
  • ContentFrame: 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">

useTabs Return Value

PropertyTypeDescription
valuestringThe currently selected tab value
setValue(value: string) => voidFunction to change the selected tab
direction'ltr' | 'rtl'The resolved text direction
tabsPropsobjectProps to spread on the tabs container
listPropsobjectProps to spread on the tab list
getTabProps(value: string, disabled?: boolean) => objectGet props for a tab trigger
getContentProps(value: string) => objectGet props for a tab content panel
contextValueTabsContextValueContext value for component-based API

useTab

Hook for individual tab triggers when using the component-based API.

tsx
const { isSelected, tabProps } = useTab({
  value: 'tab1',
  disabled: false,
  onPress: () => {},
  onKeyDown: () => {},
  onFocus: () => {},
})

useTabContent

Hook for tab content panels when using the component-based API.

tsx
const { isSelected, shouldMount, contentProps } = useTabContent({
  value: 'tab1',
  forceMount: false,
})

</Tabs.Content>

<Tabs.Content value="styled">

Tabs.List

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., }, ]} />

<Notice> 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](/docs/components/group#custom-components--nested-items) for workarounds. </Notice>

Tabs.Tab

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., }, ]} />

Tabs.Content

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., }, ]} />

Examples

Animations

Here is a demo with more advanced animations using AnimatePresence and Tab's onInteraction prop.

<HeroContainer noPad showAnimationDriverControl> <TabsAdvancedDemo /> </HeroContainer>
tsx

</Tabs.Content>

</Tabs>