Icon-only tab bar whose active tab grows its label on a spring; the panel above resizes and slides content in from the side you moved towards.
tabsnavigationtoolbardockiconpillspringdirection-aware
Installation
Pro installs use your private registry token. Add the @geturui-pro registry once — setup guide. The command works after you get Pro.
$npx shadcn@latest add @geturui-pro/expandable-tabs
Usage
usage.tsx
1import { Calendar, Inbox, Settings } from "lucide-react";
2import { ExpandableTabs } from "@/components/ui/expandable-tabs";
3
4export function WorkspaceBar() {
5 return (
6 <ExpandableTabs
7 aria-label="Workspace"
8 className="w-[380px]"
9 items={[
10 { value: "inbox", label: "Inbox", icon: <Inbox />, content: <div className="p-4">3 unread</div> },
11 { value: "calendar", label: "Calendar", icon: <Calendar />, content: <div className="p-4">2 events today</div> },
12 { value: "settings", label: "Settings", icon: <Settings />, content: <div className="p-4">Pro plan</div> },
13 ]}
14 />
15 );
16}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items* | { value: string; label: string; icon: ReactNode; content?: ReactNode }[] | — | Tabs in order. label shows in the pill and names the icon button. |
value | string | — | Controlled active tab. |
defaultValue | string | items[0].value | Active tab on mount when uncontrolled. |
onValueChange | (value: string) => void | — | Called when a tab is chosen by click or keyboard. |
showPanel | boolean | true if any item has content | Render the morphing panel above the bar. |
panelClassName | string | — | Classes for the panel surface. |
aria-label | string | — | Names the tab list. |
className | string | — | Classes for the outer column; set a width to size the panel. |
Accessibility
- role="tablist" with role="tab" buttons, aria-selected and a roving tabindex; the panel is a role="tabpanel" labelled by its tab.
- Left and Right arrows (wrapping), Home and End move focus and select; each icon button is named by its label even when collapsed.
- With reduced motion the content crossfades without sliding or blur and the panel height snaps.