KQ-1283 · TAB/STRIP

Tab Strip

A document tab strip whose tabs share one width that narrows as more open; the ones that no longer fit go into an overflow menu carrying their count, which rolls on springs.snap, and the active tab always stays in view. Every tab's slot and width are motion values on springs.glide, so a closing tab collapses while the rest are pulled left with no gap and a new tab grows out of the plus; a dragged tab lifts, leans into its speed and follows the finger 1:1 while its neighbours make room on the reorder spring. The panel cross-fades with a slide toward the side of the tab you moved to and its height glides to the new one; the tablist's arrows move and select, Shift with an arrow moves a tab, Delete closes one, and the overflow is a real menu button.

KQ-1283 · TAB/STRIP
PumpWindowFlow
North well06:00–08:3012 l/s
Weir gate09:00–11:008 l/s
Basin Road13:30–15:0015 l/s

Pump schedule · 4 open · drag a tab, press + for more

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/tab-strip

Props

tabs / defaultTabs
TabStripTab[]
default defaultStripTabs
The open tabs in order, controlled or initial: id, title, optional icon, content, dirty and pinned. defaultStripTabs is four Fernworks documents.
onTabsChange
(tabs: TabStripTab[]) => void
default —
Fires with the whole list after a close, a new tab or a reorder.
value / defaultValue
string
default the first tab's id
The active tab's id, controlled or initial.
onValueChange
(id: string) => void
default —
Fires from the press, key or menu choice that made a tab active, and when closing the active tab hands over to its neighbour.
onCreate
() => TabStripTab | void
default an empty "Untitled n"
Makes the tab the plus opens; return nothing to open none.
onClose
(id: string) => void
default —
A tab was closed, by its button, Delete or the shortcut.
label
string
default "Documents"
The tablist's accessible name.
width
number
default 168
How wide a tab is while there is room, in px; past that they all narrow together.
minWidth
number
default 96
Narrowest a tab gets before the ones that no longer fit go into the overflow menu, in px.
reorder
"glide" | "snap" | "off"
default "glide"
How the other tabs make room for a dragged one: smoothly, with one crisp overshoot, or not at all (dragging off).
slide
number
default 12
How far the panel slides as it changes, in px, toward the side of the tab you moved to; 0 cross-fades in place.
maxTabs
number
default 12
The plus is disabled at this many open tabs.
closable
boolean
default true
Close buttons on tabs and Delete to close; pinned tabs never close.
shortcuts
boolean
default false
Ctrl or Cmd with W closes the active tab and with T opens one, where the browser passes those keys on.
newLabel
string
default "New tab"
The plus button's accessible name.
size
"sm" | "md" | "lg"
default "md"
Strip height 32, 36 or 40 px.
accent
string
default "var(--accent-bright)"
The active tab's rule and the unsaved dot; any CSS colour.
sound
boolean
default false
A click for each tab chosen (pitched by its slot) and each slot a dragged tab takes, a pop as a new tab grows and a lower one as a tab closes.
disabled
boolean
default false
Disables the tabs, the plus and the menu.
className
string
default —
Classes for the strip and its panel.