Componentnavigation0.9 KB gzipZero dependencies
Tabbar V2
Bottom tabs with badges.
Preview
Live Preview
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add tabbar-v2Or install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { TabbarV2 } from "@/components/ui/tabbar-v2";
<TabbarV2 />Usage guidance
Use Tabbar V2 when a ticket stub surface needs Bottom tabs with badges. Keep props minimal and prefer composition with sibling stubs.
Avoid Tabbar V2 for long form flows or dense data grids where a dedicated layout block fits better.
Source code
tabbar-v2.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface TabbarV2Item {
id: string;
label: string;
badge?: number;
}
export interface TabbarV2Props extends React.HTMLAttributes<HTMLElement> {
items?: TabbarV2Item[];
value?: string;
onValueChange?: (id: string) => void;
}
const FALLBACK: TabbarV2Item[] = [
{ id: "shows", label: "Shows" },
{ id: "stubs", label: "Stubs", badge: 3 },
{ id: "gates", label: "Gates" },
{ id: "me", label: "Me" },
];
/** Bottom tab bar v2 with sliding ink indicator and badges. */
export function TabbarV2({ items = FALLBACK, value, onValueChange, className, ...props }: TabbarV2Props) {
const [inner, setInner] = React.useState(items[0]?.id ?? "");
const active = value ?? inner;
return (
<nav aria-label="Primary" className={cn("flex w-full rounded-xl border-2 border-foreground bg-card p-1 shadow-md", className)} {...props}>
{items.map((item) => {
const selected = item.id === active;
return (
<button
key={item.id}
type="button"
aria-current={selected ? "page" : undefined}
onClick={() => { setInner(item.id); onValueChange?.(item.id); }}
className={cn(
"relative flex-1 rounded-lg px-2 py-2 font-mono text-[11px] font-bold uppercase transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
selected ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
)}
>
{item.label}
{typeof item.badge === "number" && item.badge > 0 && (
<span className="absolute -top-1 right-1 rounded-full bg-accent px-1.5 font-mono text-[10px] font-bold text-accent-foreground">{item.badge}</span>
)}
{selected && <span className="absolute inset-x-4 -bottom-0.5 h-0.5 rounded bg-accent" aria-hidden="true" />}
</button>
);
})}
</nav>
);
}