Glide tabs

Tabs with a pill that glides under the active label.

Behaviour

  • Reduced motion: the pill moves instantly.
  • A pill glides under the active label on one spring, and picking another tab mid-glide carries on from where it is.
  • Arrow keys, Home and End move between enabled tabs, with one tab stop (roving tabindex).
  • It renders the tab bar only: show your own panel for the selected value.
  • A long list scrolls inside its own rounded bar rather than breaking the page.

Props

PropTypeDescription
tabs(string | { value, label, disabled? })[]The tabs, in order. A string is both label and value. Values must be unique.
valuestringControlled value. Leave it out and the tabs keep their own state.
defaultValuestringStarting tab when uncontrolled. Defaults to the first.
onValueChange(value: string) => voidCalled when another tab is picked.
labelstringdefault "Tabs"Accessible name for the tab list.
orientation"horizontal" | "vertical"default "horizontal"Vertical stacks the tabs and uses the up and down arrows.
idPrefixstringWires your panels: tab ids are ${idPrefix}-tab-${value} and point at ${idPrefix}-panel-${value}.
classNamestringSize comes from here, e.g. text-lg. Merged with cn().

Installation

npx shadcn@latest add https://purrfectui.com/r/glide-tabs.json

How to use

import { GlideTabs } from "@/components/ui/glide-tabs";

const [tab, setTab] = useState("preview");
<GlideTabs
  tabs={[{ value: "preview", label: "Preview" }, { value: "code", label: "Code" }, { value: "docs", label: "Docs", disabled: true }]}
  value={tab}
  onValueChange={setTab}
  idPrefix="demo"
/>
<div role="tabpanel" id={`demo-panel-${tab}`} aria-labelledby={`demo-tab-${tab}`}>…</div>

Source code

Loading…

License

MIT. Copy it into your project and change it as you like.

0