Tabs

Underline or segmented pill - ink reaches then releases; panels enter from the side you moved toward.

Tabs playground

Panels enter from the side you moved toward - go Overview to Settings and back, and watch the direction flip.

The ink reaches then releases either way - an underline that spans the tab, or a pill riding an inset track.

Install Tabs

1. New project? One command installs the package and wires everything - see Installation:

bash
pnpm dlx zyncat-ui init

2. Import the component - it loads its own stylesheet:

tsx
import { Tabs } from '@zyncat/ui/tabs';

Usage

tsx
import { Tabs, TabPanel } from '@zyncat/ui/tabs';
 
<Tabs
items={items}
value={active}
onChange={(next, d) => { setActive(next); setDir(d); }}
name="views"
ariaLabel="Workspace sections"
/>
<TabPanel name="views" tab={active} dir={dir}>
{panels[active]}
</TabPanel>

Compose Tabs in your application. No Tailwind or external styling library is required; every value resolves from Zyncat UI's token vocabulary.

Tabs props

Tabs

PropTypeDefaultDescription
...htmlAttributesHTMLAttributes—Standard <div> attributes (data-*, aria-*, ...) forwarded to the root.
items*TabItem[][]The tab row, in order.
value*string | null | undefined—Controlled - the value of the active tab (null/undefined hides the ink).
onChange(value: string, dir: 1 | -1) => void—Fires on click and on arrow-key travel. `dir` is the direction of travel (+1 right, -1 left); hand it to TabPanel so content enters from that side.
namestring—Shared id prefix wiring tab and panel aria. Give Tabs and its TabPanel the same `name`; omit it when the tabs have no managed panel.
ariaLabelstring—Accessible name for the tablist (e.g. "Section views").
activateOnActivateOn'pointerdown'Whether a tab switches on `pointerdown` (snappier) or waits for `click`.
variant'underline' | 'pill'—Skin: an underline that reaches across (default), or a segmented pill riding an inset track.
size'sm' | 'md' | 'lg''sm'Tab height, type and padding. `'md'` and `'lg'` match `Button`'s steps; the default row sits just under them.
fillboolean—Stretch the row to its container and split it evenly between the tabs.
classNamestring—Extra class(es) merged onto the root.
styleCSSProperties—Inline styles merged onto the root.

TabItem

PropTypeDefaultDescription
value*string—Stable identity - also used in the tab/panel id pair.
label*ReactNode—Visible tab text.
iconReactNode—Leading icon - your own node.
countnumber | string—Rendered mono + tabular. Pass a number or a preformatted string.
disabledboolean—Dims the tab, blocks selection, and skips it during arrow-key travel.
Set in Geist & Newsreader — animated by the house engineZyncat UI · Rev 0.11 · MIT · Built by Tabsir Ahammed · Source on GitHub