Liquid tab barPro

Tabs with a liquid selection that stretches between them and fills in icons as it passes.

Live · keyboard ready

Good morning, Ryan

Your next trip is in 19 days

Half Dome above a meadow in Yosemite Valley
Yosemite ValleyOct 11 to 13, 3 friends
  • Switching between a handful of peer sections with icons, such as an app's main areas.
  • Bottom or top bars on mobile-style layouts where the selection should feel physical.
  • Tabs that carry badge counts for new items.
  • Use tabs for text-only panels inside a page.
  • Use segmented-control for compact filters or view toggles.
  • Use morph-nav for site navigation with dropdown panels.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { LiquidTabBar, liquidPanelId, liquidTabId } from "@/registry/components/liquid-tab-bar/liquid-tab-bar";import { Home, Library, Radio } from "lucide-react"; export function Sections() {  const [tab, setTab] = useState("home");  return (    <>      <LiquidTabBar id="nav" value={tab} onValueChange={setTab} tabs={[        { value: "home", label: "Home", icon: <Home /> },        { value: "radio", label: "Radio", icon: <Radio />, badge: 2, badgeLabel: "2 new" },        { value: "library", label: "Library", icon: <Library /> },      ]} />      <section id={liquidPanelId("nav", tab)} role="tabpanel" aria-labelledby={liquidTabId("nav", tab)}>{/* ... */}</section>    </>  );}

API reference

2 parts. The first is the root.

LiquidTabBar

Horizontal navigation for a handful of peer sections. The selection is a liquid lens that stretches toward the new tab and redraws what passes under it inverted, so outline icons fill in.

PropTypeDefaultDescription
tabsRequired{ value: string; label: string; icon: ReactNode; activeIcon?: ReactNode; badge?: number; badgeLabel?: string }[]–Tabs in order. activeIcon is drawn inside the lens (defaults to icon filled in); badge is a rolling count and badgeLabel what screen readers hear with it.
valueRequiredstring–Selected tab value.
onValueChangeRequired(value: string) => void–Called when a tab is chosen by tap, drag, or keyboard.
labels"active" | "always" | "none""active"Which tabs show their label: the selected one, every tab, or none.
labelstring"Sections"Accessible name of the tab list.
idstring–Base id for tab and panel ids. Defaults to a generated id.
classNamestring–Extra class on the tab list.

liquidTabId / liquidPanelId

Helpers that return a tab's id and the panel id its aria-controls points at, so panels can be wired up.

PropTypeDefaultDescription
idRequiredstring–The base id passed to LiquidTabBar.
valueRequiredstring–The tab value.
ArrowLeftorArrowRight
Selects the previous or next tab and moves focus to it, wrapping at the ends.
HomeorEnd
Selects the first or last tab.
  • Uses the tablist and tab roles with aria-selected, aria-controls, and a roving tabindex.
  • Each tab's accessible name includes its badge, using badgeLabel when given.
  • The inverted copy inside the lens is decorative; only one set of tabs is exposed.
  • The lens's leading edge races to the new tab while the trailing edge catches up, so it stretches with distance and speed and thins as it stretches; it leans toward a hovered tab.
  • Grab the lens on the selected tab and drag or throw it; it lands where its momentum reaches, a quick flick always moves at least one tab, and it resists past either end. The selected tab's label opens on a spring and badge digits roll.
  • Reduced motion jumps the lens and labels to their places without stretch or lean.
  • Slots are a fixed 44px plus the open label, and the bar caps at max-width 100%; set labels to none when space is tight.
  • The bar uses touch-action pan-y, so dragging the lens sideways works on touch without blocking page scroll.
  • Hover lean follows the mouse only; touch selects on tap or throw.
  • The lens animates a clip-path over an inverted copy of the tabs, so every tab is rendered twice.
  • Label widths are measured from hidden copies whenever tabs change; keep the tab count to a handful.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Choose it for switching between a handful of peer sections with icons, such as an app's main areas. Use tabs for text-only in-page panels and segmented-control for compact filters.
  • It is controlled: keep value in state. Wire panels with liquidPanelId and liquidTabId using the same id prop.

The full library index for assistants is at /llms.txt.