Tabs
A set of layered sections of content—known as tab panels—that are displayed one at a time. Pro version includes dynamic tab management, advanced animations, state persistence, and more.
Pro features
What the Pro version adds to this component.
Premium Style Variants
8 distinctive styles including pills, underline, gradient, glassmorphism, and neon
Advanced Animations
6 animation modes for smooth transitions between tab panels
Dynamic Management
An items array your app can drive, closeable tabs, an add affordance, and drag-along-the-strip reordering — every change reported through onTabsChange
Smart Features
State persistence, URL sync, keyboard shortcuts, and horizontal/vertical orientation
Installation
Install via CLI
npx @moontra/moonui-cli add tabsUsage
Basic Usage
Content for tab 1
Pro Usage
Examples
Basic Tabs
Simple tabs with content panels
With Icons
Tabs with icons for better visual hierarchy
Variants & Sizes
Style tabs with the built-in CVA props: TabsList variant, TabsTrigger variant/size, the icon & iconPosition props, fadeTabs, and animated content
Vertical Orientation
Settings-style layouts where the labels are long and the panel is tall: set orientation="vertical" on the root only — TabsList and TabsTrigger read it from context, so arrow-key direction and layout always agree (a child can still override it locally)
Advanced Animations
Rich animations with multiple variants
Closeable Tabs (items API)
Editor-style workspaces where the user opens and dismisses views. Describe the tabs with the items array and turn on closeable — the component renders the close control itself, as a properly labelled button next to the tab (never nested inside it), and keeps a pinned tab open with closeable: false. size="sm" keeps a long tab strip compact. Here the component owns the strip after the first render and reports every dismissal through onTabClose / onTabsChange; the next example shows the opposite arrangement, where the array lives in your state and the component follows it
Parent-Controlled Tabs (items)
The opposite arrangement: the tab array lives in your own state and the component follows it. Push a new items array — one more tab, a renamed one, one fewer — and the strip updates. The comparison is by content rather than by array identity, so an inline items={[...]} does not wipe the strip on every render: what the user did in the meantime (closing a tab) survives until you actually hand over different data. Mirror onTabsChange back into your state to keep the two in step
Reorderable Tabs (draggable)
Turn on draggable and a tab can be dragged along the strip — on the X axis when the tabs are horizontal, on the Y axis when they are vertical. The tab under the pointer at the moment you let go becomes the target position; release outside the strip and the order is left alone. Every landed reorder is reported through onTabsChange. Dragging is a pointer-only gesture, so this example also wires the same reordering to two buttons with the exported reorderTabItems helper — that is the keyboard path your users will need
State Persistence
URL sync and localStorage persistence