Skip to content
All Taste components

Navigation

Bottom tab bar

The main tabs of a mobile app, pinned to the bottom of the screen within thumb reach.

active tab · centre action · unread badge · home indicator

Thursday, 16 Oct

Today

Deep clean · 2BHK
10:00 · Indiranagar
Confirmed
Sofa shampoo
14:30 · HSR Layout
On the way
import { Calendar, Home, Inbox, Plus, User, type LucideIcon } from "lucide-react";

type Tab = { href: string; label: string; icon: LucideIcon; badge?: number };

const tabs: Tab[] = [
  { href: "/", label: "Home", icon: Home },
  { href: "/bookings", label: "Bookings", icon: Calendar },
  { href: "/inbox", label: "Inbox", icon: Inbox, badge: 2 },
  { href: "/profile", label: "Profile", icon: User },
];

export function TabBar({ current }: { current: string }) {
  const left = tabs.slice(0, 2);
  const right = tabs.slice(2);
  return (
    <nav aria-label="Main" className="fixed inset-x-0 bottom-0 border-t border-border bg-bg pb-[max(env(safe-area-inset-bottom),8px)] pt-1.5">
      <div className="mx-auto grid max-w-md grid-cols-5">
        {left.map((t) => <TabLink key={t.href} tab={t} active={current === t.href} />)}
        <a href="/bookings/new" aria-label="New booking" className="grid place-items-center">
          <span className="grid size-11 place-items-center rounded-lg bg-primary text-primary-fg"><Plus className="size-[22px]" aria-hidden /></span>
        </a>
        {right.map((t) => <TabLink key={t.href} tab={t} active={current === t.href} />)}
      </div>
    </nav>
  );
}

function TabLink({ tab, active }: { tab: Tab; active: boolean }) {
  const Icon = tab.icon;
  return (
    <a
      href={tab.href}
      aria-current={active ? "page" : undefined}
      className="relative grid justify-items-center gap-[3px] py-1.5 text-[11px] font-medium text-fg-subtle aria-[current=page]:text-primary"
    >
      <Icon className="size-[22px]" aria-hidden />
      {tab.label}
      {tab.badge ? (
        <span className="absolute left-[calc(50%+4px)] top-0.5 grid h-4 min-w-4 place-items-center rounded-full bg-danger px-1 text-[10px] font-semibold text-bg ring-2 ring-bg">
          {tab.badge}
        </span>
      ) : null}
    </a>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.