Pre-alphaThe registry and the CLI are not published yet.Roadmap

Tab

Presentational top tabs with controlled selection, badges, scrollable layouts and optional swipe between panels.

Activity
AllMentionsRequests

2

Maya mentioned youIn Launch checklist · 4m
Jon completed a taskHomepage review · 21m
Nora requested accessBrand assets · 1h

Installation

npx axiom add tab
pnpm dlx axiom add tab
yarn dlx axiom add tab
bun x axiom add tab

Also copies: icon, badge, text, tappable and useControllableState. Installs react-native-reanimated.

Colors come from tab.{underline,pill}.{default,selected,disabled} in the component tokens.

Usage

import { Tab } from "@/components/ui/tab";

<Tab value={section} onValueChange={setSection}>
	<Tab.List>
		<Tab.Item value="all">All</Tab.Item>
		<Tab.Item value="unread">Unread</Tab.Item>
		<Tab.Item value="archived">Archived</Tab.Item>
	</Tab.List>

	<Tab.Content>
		<Tab.Panel value="all">
			<Feed />
		</Tab.Panel>
		<Tab.Panel value="unread">
			<UnreadFeed />
		</Tab.Panel>
		<Tab.Panel value="archived">
			<Archive />
		</Tab.Panel>
	</Tab.Content>
</Tab>;

Props

Tab

PropTypeDefaultDescription
valuestring—Value of the selected tab in controlled usage.
defaultValuestring—Initial selection when the component owns its state.
onValueChange(value: string) => void—Called after an enabled tab is pressed. Use it to swap nearby content, not to push a new screen.
hapticHapticKind | false—Haptic played when the user changes the tab, by tap or swipe. Off unless you pass a kind.
childrenReactNode—A Tab.List and optional content.
View propsViewProps—Passed to the root View, including style, accessibility props and ref.

Tab.List

Tab.List renders a View by default. With scrollable, it renders a horizontal ScrollView and keeps each item at its natural width.

PropTypeDefaultDescription
variant'underline' | 'pill''underline'Uses an indicator below the label or a filled capsule behind it.
scrollablebooleanfalseChanges the list from View to ScrollView.
View or ScrollView propsViewProps | ScrollViewProps—Passed to the rendered native component. contentContainerStyle is available when scrollable is true.

Tab.Item

PropTypeDefaultDescription
valuestring—Stable identifier returned through onValueChange.
childrenReactNode—Short label. One or two words scan best.
iconIconName | ReactNode—Optional icon before the label. Avoid icons when every tab is already clear in text.
badgenumber | string | boolean—Count, short status or dot next to the label.
disabledbooleanfalseBlocks selection and exposes the disabled accessibility state.
accessibilityLabelstringlabel textSpoken label when the visible text needs more context.
Tappable propsTappableProps—Passed to the press target. Internal press and layout handlers are composed with yours.

Tab.Content

Tab.Content is a View. It renders only the selected Tab.Panel.

PropTypeDefaultDescription
View propsViewProps—Passed to the content View.

Tab.Pager

Tab.Pager is a horizontal ScrollView with pagingEnabled. Use it instead of Tab.Content when users should be able to swipe between panels.

PropTypeDefaultDescription
ScrollView propsScrollViewProps—Passed to the pager, except onScroll. Internal layout and momentum handlers are composed with yours.

Tab.Panel

PropTypeDefaultDescription
valuestring—Value of the Tab.Item this panel belongs to.
childrenReactNode—Content shown while that tab is selected.
View propsViewProps—Passed to the panel View. Its layout handler is composed with yours.

The indicator travels on a spring, and lands without animating the first time: it has nothing to travel from until the items have been measured. Reduced motion moves it without animation. Inside a Tab.Pager, the indicator follows the finger instead: its position and width track the pager’s scroll on the UI thread.

Tab sets the tab list, tab and selected accessibility roles. Panels are optional. Without Tab.Content or Tab.Pager, the screen can render its content elsewhere from the selected value.

Haptics

Tabs play nothing by default. Pass a kind to Tab, usually "selection", to play one when the user changes the tab, by pressing an item or by swiping the pager. A value changed in code plays nothing. Nothing plays until your app connects a haptics library: see Haptics.

Swipe to switch

Use Tab.Pager when the panels contain peer views such as feeds, inboxes or order lists. Use Tab.Content when a panel owns horizontal interaction, for example a carousel, a pannable chart or rows with swipe actions.

<Tab value={section} onValueChange={setSection} style={{ flex: 1 }}>
	<Tab.List>
		<Tab.Item value="all">All</Tab.Item>
		<Tab.Item value="mentions">Mentions</Tab.Item>
	</Tab.List>

	<Tab.Pager>
		<Tab.Panel value="all">
			<Feed />
		</Tab.Panel>
		<Tab.Panel value="mentions">
			<Mentions />
		</Tab.Panel>
	</Tab.Pager>
</Tab>

The pager needs a bounded width and height. It uses the native ScrollView paging behavior, measures each panel at runtime and updates the selected value when scrolling settles. Pressing a tab scrolls to the measured position of its panel. Off-screen panels stay hidden from the screen reader.

Tab.Pager passes its props to the underlying ScrollView, so scrollEnabled, decelerationRate, keyboard behavior and the momentum callbacks remain configurable. onScroll is the exception: the pager’s scroll drives the indicator, so it keeps that handler for itself.

Use cases

Switching one data view

Tabs work when the content changes but the screen purpose stays the same. Keep the filter state local to the screen.

Orders
ActivePast
Order #1842

Shipped

Arrives Thursday · $88.90
Order #1831

Preparing

3 items · $126.00
<Tab value={status} onValueChange={setStatus}>
	<Tab.List>
		<Tab.Item value="active">Active</Tab.Item>
		<Tab.Item value="past">Past</Tab.Item>
	</Tab.List>
</Tab>

Categories that need horizontal scrolling

When labels cannot fit without shrinking, let the row scroll. The selected tab should be brought into view after selection.

Discover
For youTechnologyDesignScienceCulture
A smaller battery with a longer life8 min read
<Tab value={category} onValueChange={setCategory}>
	<Tab.List scrollable>
		{categories.map((item) => (
			<Tab.Item key={item.id} value={item.id}>
				{item.label}
			</Tab.Item>
		))}
	</Tab.List>
</Tab>

Compact mode switch with pills

The pill variant works for a short, local mode switch. For two or three equally sized options inside a form, prefer SegmentedControl.

Saved

Grid

Map

<Tab value={view} onValueChange={setView}>
	<Tab.List variant="pill">
		<Tab.Item value="grid" icon="grid">
			Grid
		</Tab.Item>
		<Tab.Item value="map" icon="map">
			Map
		</Tab.Item>
	</Tab.List>
</Tab>