Skip to main content
Clever Ops

Tab Rail Navbar

Two-deck React navbar, primary sections over a persistent contextual sub-link rail with a sliding Tailwind underline; the mobile rail scrolls its own overflow with edge fades.

Preview & code

Loading Tab Rail Navbar preview

Installation

pnpm dlx shadcn@latest add @cleverui/navbar-tab-rail

Requires the @cleverui registry in your components.json, a one-off, two-line setup.

Usage

tsx
import { NavbarTabRail } from '@/components/blocks/navbar-tab-rail'
Exampletsx
'use client'

import { useEffect, useState } from 'react'
import { NavbarTabRail } from '@/components/blocks/navbar-tab-rail'

const SECTIONS = [
  {
    label: 'Catalog',
    href: '#catalog',
    items: [
      { label: 'Overview', href: '#catalog-overview' },
      { label: 'Collections', href: '#catalog-collections' },
      { label: 'Inventory', href: '#catalog-inventory' },
      { label: 'Price lists', href: '#catalog-prices' },
    ],
  },
  {
    label: 'Orders',
    href: '#orders',
    items: [
      { label: 'Open', href: '#orders-open' },
      { label: 'Fulfilled', href: '#orders-fulfilled' },
      { label: 'Returns', href: '#orders-returns' },
      { label: 'Invoices', href: '#orders-invoices' },
    ],
  },
  {
    label: 'Customers',
    href: '#customers',
    items: [
      { label: 'Directory', href: '#customers-directory' },
      { label: 'Segments', href: '#customers-segments' },
      { label: 'Loyalty', href: '#customers-loyalty' },
    ],
  },
  {
    label: 'Reports',
    href: '#reports',
    items: [
      { label: 'Revenue', href: '#reports-revenue' },
      { label: 'Velocity', href: '#reports-velocity' },
      { label: 'Channels', href: '#reports-channels' },
      { label: 'Exports', href: '#reports-exports' },
    ],
  },
  {
    label: 'Settings',
    href: '#settings',
    // No items, rail collapses
  },
]

const COPY: Record<string, { title: string; body: string }> = {
  '#catalog-overview': {
    title: 'Catalog overview',
    body: '1,842 SKUs across 14 collections. 38 need photography refresh before the fall lookbook ships.',
  },
  '#catalog-collections': {
    title: 'Collections',
    body: 'Walnut Desk System leads units this quarter. Brass Hardware Kit is 12 days from stockout at current velocity.',
  },
  '#catalog-inventory': {
    title: 'Inventory positions',
    body: 'Oakland DC holds 64% of units. Transfer 40 desk legs to Denver before the trade-show allocation freezes.',
  },
  '#catalog-prices': {
    title: 'Price lists',
    body: 'Trade A is live through September. MSRP sheet v4.2 includes the new matte nickel finish family.',
  },
  '#orders-open': {
    title: 'Open orders',
    body: '27 open, 6 awaiting payment confirmation. Largest: Meridian Studio, 48 desk legs, white-glove, dock code MS-441.',
  },
  '#orders-fulfilled': {
    title: 'Fulfilled this week',
    body: '119 shipments, average transit 2.4 days. Two white-glove reschedules on the East Bay run.',
  },
  '#orders-returns': {
    title: 'Returns queue',
    body: 'Four open RMAs. Two are finish mismatches, photography brief already filed with studio ops.',
  },
  '#orders-invoices': {
    title: 'Invoices',
    body: 'Net-30 aging: $48.2k under 15 days, $6.1k at 30+. Atlas Cloud paid early again.',
  },
  '#customers-directory': {
    title: 'Customer directory',
    body: '612 active accounts. 41 new this month, mostly interior studios under 20 seats.',
  },
  '#customers-segments': {
    title: 'Segments',
    body: 'Trade A accounts drive 58% of revenue. Residential gift buyers peak Fridays after lookbook emails.',
  },
  '#customers-loyalty': {
    title: 'Loyalty ledger',
    body: 'Studio credits expire end of quarter. Top earner: Field & Form, 14,200 points, one free freight voucher.',
  },
  '#reports-revenue': {
    title: 'Revenue',
    body: 'MTD $412k, +9% vs prior. Desk systems alone cover 44% of the line.',
  },
  '#reports-velocity': {
    title: 'Sell-through velocity',
    body: 'Brass hardware moved 2.1× faster after the material sample pack launched.',
  },
  '#reports-channels': {
    title: 'Channels',
    body: 'Wholesale 61%, direct 28%, marketplace 11%. Marketplace returns run 1.8× wholesale.',
  },
  '#reports-exports': {
    title: 'Exports',
    body: 'CSV and JSON available nightly. Warehouse EDI drops at 02:00 PT to Oakland and Denver.',
  },
  '#settings': {
    title: 'Workspace settings',
    body: 'Team seats, tax profiles, and dock appointment defaults. No sub-rail, this section stands alone.',
  },
}

export default function NavbarTabRailDemo() {
  const [activeSection, setActiveSection] = useState('#catalog')
  const [activeItem, setActiveItem] = useState('#catalog-overview')

  const section = SECTIONS.find((s) => s.href === activeSection)
  const pageKey =
    section?.items && section.items.length > 0 ? activeItem : activeSection
  const page = COPY[pageKey] ?? {
    title: section?.label ?? 'Workspace',
    body: 'Select a section or sub-link in the rail above.',
  }

  // Demo-only: keep the rail keyline in sync without a real router
  useEffect(() => {
    const onClick = (e: MouseEvent) => {
      const anchor = (e.target as HTMLElement).closest('a')
      if (!anchor) return
      const href = anchor.getAttribute('href')
      if (!href || !href.startsWith('#')) return
      if (href === '#invite') return

      e.preventDefault()

      const matchedSection = SECTIONS.find((s) => s.href === href)
      if (matchedSection) {
        setActiveSection(matchedSection.href)
        const first = matchedSection.items?.[0]?.href
        if (first) setActiveItem(first)
        return
      }

      for (const s of SECTIONS) {
        const item = s.items?.find((i) => i.href === href)
        if (item) {
          setActiveSection(s.href)
          setActiveItem(item.href)
          return
        }
      }
    }
    document.addEventListener('click', onClick)
    return () => document.removeEventListener('click', onClick)
  }, [])

  return (
    <div className="min-h-[640px] bg-background">
      <NavbarTabRail
        brand={
          <span className="inline-flex items-baseline gap-1.5">
            Northline
            <span className="font-mono text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
              ops
            </span>
          </span>
        }
        sections={SECTIONS}
        activeSection={activeSection}
        activeItem={activeItem}
        onSectionChange={(href) => {
          setActiveSection(href)
          const next = SECTIONS.find((s) => s.href === href)
          const first = next?.items?.[0]?.href
          if (first) setActiveItem(first)
        }}
        cta={{ label: 'Invite teammate', href: '#invite' }}
      />

      <div className="mx-auto max-w-3xl px-4 py-10 sm:px-6">
        <p className="font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground sm:text-xs">
          {section?.label ?? 'Workspace'}
          {section?.items?.some((i) => i.href === activeItem)
            ? ` · ${section.items.find((i) => i.href === activeItem)?.label}`
            : ''}
        </p>
        <h1 className="mt-3 text-balance text-2xl font-semibold tracking-tight sm:text-3xl">
          {page.title}
        </h1>
        <p className="mt-3 max-w-xl text-sm leading-relaxed text-muted-foreground sm:text-base">
          {page.body}
        </p>

        <div className="mt-10 space-y-3">
          {[
            'Oakland DC · cycle count due Friday',
            'Trade A price list · review before Sep 1',
            'White-glove slots · 4 remaining this week',
          ].map((line) => (
            <div
              key={line}
              className="rounded-2xl border border-border/60 bg-card px-5 py-4 text-sm text-muted-foreground"
            >
              {line}
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

API reference

NavbarTabRail accepts the following props.

PropTypeDefaultDescription
brandReact.ReactNode'CleverUI'
brandHrefstring'/'
sections*NavbarTabRailSection[]top-deck destinations; active section's items populate the sub-rail
activeSectionstringhref of the current section, defaults to first
activeItemstringhref of the current sub-link; receives aria-current and the underline
onSectionChange(href: string) => voidSPA hook fired alongside section anchor navigation
cta{ label: string; href: string }

…plus everything from React.HTMLAttributes<HTMLElement>: className, event handlers, aria attributes and the rest pass straight through.

Referenced types

Typestsx
export interface NavbarTabRailSection {
  label: string
  href: string
  items?: NavbarTabRailItem[]
}

export interface NavbarTabRailItem {
  label: string
  href: string
}

Frequently asked questions

How do I install the Tab Rail Navbar component?

Register the @cleverui registry in your components.json ("@cleverui": "https://cleverops.com.au/r/{name}.json"), then run `npx shadcn@latest add @cleverui/navbar-tab-rail`. The shadcn CLI copies the source into your project, you own the code from that point. You can also copy the source directly from this page.

What dependencies does Tab Rail Navbar require?

Tab Rail Navbar uses motion and lucide-react on top of React and Tailwind CSS. You don't need to install them manually, the shadcn CLI resolves and installs npm dependencies automatically when you add the component.

Can I theme Tab Rail Navbar to match my brand?

Yes. The component is styled entirely with Tailwind utilities over standard design tokens (primary, background, muted, border), so it inherits your existing shadcn/ui theme automatically. Change your CSS variables and the component follows, no source edits needed for palette, radius, or fonts.

Want this built for you?

CleverUI is built by the Clever Ops web team. If you'd rather ship a hand-coded, fast, custom website than assemble one, we build them for Australian businesses every week.

Explore web development services