Vibe Code
RegistryFluid Tabs

Fluid Tabs

Tabs that expand when clicked. Revolutionary stuff.

Spring physics so smooth it'll make your other components look choppy. Not my problem.

bunx --bun shadcn@latest add @vcode/fluid-tabs

Live Demo

Click them. They expand. Shocking, I know.

Basic

Icons. Labels. Spring physics. Done.

With Notifications

Badges that wiggle on hover. Totally necessary.

Vertical

For sidebars that refuse to be boring.

Custom Colours

Make it match your brand. Or don't. I'm not your boss.

Disabled State

Some tabs just aren't ready for the world yet.

Controlled

Full control. Try not to break anything.

Open on Hover

Hover to expand. Feels expensive.

Size Variants

Small, medium, large. Like ordering coffee but for tabs.

Features

Fluid Motion

Spring physics tuned better than your Spotify algorithm. Labels flow, badges wiggle. Zero jank guaranteed.

Smart Badges

Notification counts that wiggle on hover. Over 99? Becomes "99+" because nobody counts past that anyway.

Flexible Layout

Horizontal or vertical. Dividers or not. Custom colours. Three sizes. Do whatever you want.

Installation

The Easy Way

One command. Everything sorted. Enjoy your free time.

bunx --bun shadcn@latest add @vcode/fluid-tabs

The Hard Way

For people who enjoy suffering. Configure the registry first:

components.json
{
  "registries": {
    "@vcode": "https://ui.vcode.sh/r/{name}.json"
  }
}

Install dependencies manually:

bun add motion lucide-react

Then add the component:

bunx --bun shadcn@latest add @vcode/fluid-tabs

Usage

Basic Usage

Icons, labels, done. Nothing fancy.

import { Home, Bell, Settings } from "lucide-react";
import { FluidTabs } from "@/components/ui/fluid-tabs";

export function Example() {
  const items = [
    { label: "Dashboard", icon: Home },
    { label: "Notifications", icon: Bell, notification: 5 },
    { label: "Settings", icon: Settings },
  ];

  return <FluidTabs items={items} />;
}

With Notifications

Slap on a notification prop. Numbers or strings, whatever.

const items = [
  { label: "Inbox", icon: Inbox, notification: 12 },
  { label: "Archive", icon: Archive, notification: 3 },
  { label: "Trash", icon: Trash2, notification: "!" },
];

<FluidTabs items={items} />

Vertical Direction

Sidebars exist. Set direction="column" and you're sorted.

<FluidTabs
  items={items}
  direction="column"
/>

Custom Colours

Make it yours with highlightColor and className. Or steal someone else's palette.

<FluidTabs
  items={items}
  highlightColor="text-emerald-500"
  className="border-emerald-200 dark:border-emerald-800"
/>

With Dividers

Group your tabs. Or don't. The dividers won't judge.

const items = [
  { label: "Home", icon: Home },
  { label: "Settings", icon: Settings },
  { type: "divider" as const },
  { label: "Help", icon: HelpCircle },
  { label: "Logout", icon: LogOut },
];

Size Variants

Small, medium, large. Like t-shirts but for UI components. Default is medium because we're not monsters.

<FluidTabs items={items} size="sm" />
<FluidTabs items={items} size="md" />
<FluidTabs items={items} size="lg" />

Open on Hover

Expand on hover with configurable delay. Debounced for smooth performance. 75ms default feels right, but you do you.

<FluidTabs
  items={items}
  openOnHover={true}
  hoverDelay={75}
/>

API Reference

FluidTabs Props

PropTypeDefaultDescription
itemsFluidTabItem[]-Array of tabs and dividers
highlightColorstring"text-primary"Tailwind color class for active tab
direction"row" | "column""row"Layout direction
initialTabnumber | nullnullInitially selected tab index
collapseOnClickAwaybooleantrueCollapse when clicking outside
onTabChange(index: number | null) => void-Callback when selection changes
size"sm" | "md" | "lg""md"Size variant for tabs
rotateIconbooleanfalseEnable 360° icon rotation on activation
openOnHoverbooleanfalseExpand tabs on mouse hover instead of click
hoverDelaynumber75Delay in milliseconds before hover triggers expansion

FluidTab Item

PropertyTypeRequiredDescription
labelstringYesText shown when expanded
iconLucideIconYesLucide icon component
notificationnumber | stringNoNotification badge content
isDisabledbooleanNoDisable tab interaction
type"divider"For dividersCreates visual divider

Still reading?

Go build something. These tabs won't install themselves.