Skip to content

Tabs

Switch between related panels of content in the same space.

import { Tabs, TabsContent, TabsList, TabsTrigger } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use Tabs for related views, such as a project’s overview, deployments and settings. Each tab shows its own panel. To choose a value or filter, use a segmented Toggle Group. To navigate between pages, use links in a Sidebar or Navigation Menu.

import { Tabs, TabsContent, TabsList, TabsTrigger } from '@youj/react'

export function ProjectTabs() {
  return (
    <Tabs defaultValue="overview">
      <TabsList aria-label="Project">
        <TabsTrigger value="overview">Overview</TabsTrigger>
        <TabsTrigger value="deployments">Deployments</TabsTrigger>
        <TabsTrigger value="settings">Settings</TabsTrigger>
      </TabsList>
      <TabsContent value="overview">…</TabsContent>
      <TabsContent value="deployments">…</TabsContent>
      <TabsContent value="settings">…</TabsContent>
    </Tabs>
  )
}

Each TabsTrigger selects the TabsContent with the same value. Name the list with aria-label or aria-labelledby, so screen readers announce what the tabs switch between. To control the selection, pass value and onValueChange to Tabs.

The default underline list is plain labels on a hairline: muted until hovered, the selected one in full ink over a 2px accent bar that glides between tabs. It looks the same in every component style. Move across the tabs in the Demo story, or Tab into them and press ← and →.

Selection and focus

By default the arrow keys move focus and select in one step (activateOnFocus), which the ARIA Authoring Practices recommend when panels show without delay. If a panel fetches data or takes a while to render, turn it off: the arrows then only move focus, and Enter or Space selects.

<TabsList aria-label="Reports" activateOnFocus={false}>

A disabled tab stays focusable, so people can discover it, but it's never selected.

Reference

Propsvariant, activateOnFocus, inset (3)

Props

TabsList

TabsList props
PropType
variantTabsVariant
'underline''pill'

default 'underline'

'underline'
activateOnFocusboolean

Selection follows focus as the arrow keys move.

default true

true
insetboolean

For a horizontal list that spans its container, edge to edge: keep the tabs on the content's inset, --tabs-inset (set it on the list; default the card's --card-spacing, else 1rem). Underline tabs line their labels up with it and their hairline still spans; a track (pill tabs) lines its edge up with it.

default false

false
Other props pass through: extends TabsPrimitive.List.Props.

Tabs, TabsTrigger and TabsContent

These pass their props through to Base UI's Tabs.Root, Tabs.Tab and Tabs.Panel. The ones you'll use most:

PartPropTypeDefault
TabsdefaultValueany0
Tabsvalue, onValueChangeany, (value, details) => void–
Tabsorientation'horizontal' | 'vertical''horizontal'
TabsTriggervalue (required)any–
TabsTriggerdisabledbooleanfalse
TabsContentvalue (required)any–
TabsContentkeepMountedbooleanfalse
MotionIndicator, panel… · reduced motion

Motion

Motion tokens
MomentTokens
Indicatorglides to the selected tab, transform only
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Panelfades in with a 4px drift from the side the selection moved to
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Tab color
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The indicator jumps to the selected tab, panels appear without a fade or drift, and a long list scrolls to the selected tab without smoothing.

The outgoing panel hides at once, so two panels never share the space. A new selection mid-glide retargets the indicator from where it is, keeping its velocity. Resizes, font loads and right-to-left layouts re-place the indicator without animating.

Reduced motion

Selection works exactly the same. The indicator jumps instead of gliding, the panel appears without its fade and drift, and a long list jumps to the selected tab instead of scrolling smoothly. Switch on Reduced motion in any preview to compare.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • The list is a tablist, named by its aria-label. Each trigger is a tab with aria-selected, and each panel is a tabpanel labeled by its tab.
  • The list is one tab stop. Tab moves from the selected tab into the panel.
  • The indicators and the scroll chevrons are hidden from assistive technology. The chevrons are pointer-only and never take focus, because the focused tab scrolls itself into view.
KeyAction
← →Move to the previous or next tab and select it. Focus loops at the ends.
↑ ↓The same, in a vertical list.
Home / EndMove to the first or last tab.
Enter / SpaceSelect the focused tab, when activateOnFocus is off.
TabLeave the list for the panel.