Ship-It Designv0.0.20

GitHub

NavBar

Primary app navigation. The same component renders either horizontally (top bar) or vertically (side rail), driven by one items tree. Items can carry nested children to produce dropdowns on horizontal and expand-collapse groups on vertical. Below the md breakpoint the bar collapses to a hamburger that opens a Drawer containing the items — opt out with responsive={false}.

The data shape#

items is a recursive tree of NavBarItem. Items without href render as <button> (good for in-app navigation routed by state); items with href render as <a> (good for SEO + native link behavior); items with children produce a dropdown on horizontal or an expand group on vertical.

tsx
const items: NavBarItem[] = [
  { id: 'home', label: 'Home', href: '/' },
  {
    id: 'products',
    label: 'Products',
    children: [
      { id: 'a', label: 'Product A', href: '/a' },
      { id: 'b', label: 'Product B', href: '/b' },
    ],
  },
];

Horizontal#

The default layout. Brand on the left, items in the middle, actions on the right. Items without children render as plain links; items with children open a dropdown via @radix-ui/react-navigation-menu.

Loading…

Vertical#

Same data, side rail. Groups with children expand inline. The group auto-opens when one of its descendants is the active item, so deep links land on a discoverable item without a manual click.

Loading…

With submenus (horizontal)#

Top-level items can declare children to open a dropdown. Disabled children are still rendered but skip activation.

Loading…

With expandable groups (vertical)#

Same children shape — the vertical layout shows them as collapsible groups instead of dropdowns. Active state propagates to the parent trigger so the visual hierarchy matches what the user is looking at.

Loading…

Item (NavBarItem)#

A recursive shape — each item can carry its own children of the same type.

  • id: string (required) — stable identifier. What value / onValueChange reference, and used to walk the tree for active-descendant detection.
  • label: ReactNode (required) — visible label.
  • icon?: ReactNode — optional left-of-label icon node.
  • href?: string — when set, the item renders as <a> and fires native navigation alongside onValueChange. Without it the item renders as <button>.
  • badge?: ReactNode — trailing badge text (often a count, "new", or status indicator).
  • disabled?: boolean — renders the item but skips activation. Keyboard navigation passes over it.
  • children?: NavBarItem[] — nested items. Dropdowns on horizontal; expand-collapse groups on vertical.

Active state#

Active state is either controlled via value + onValueChange, or uncontrolled via defaultValue. An item is "active" when its id matches value; for groups, the trigger is treated as active when any descendant is active.

tsx
const [active, setActive] = useState('overview');
 
<NavBar items={items} value={active} onValueChange={setActive} />;

Items with hrefs#

When an item has an href, it renders as an <a> and onValueChange fires alongside the native link navigation — convenient for keeping a "current page" highlight in sync with router-driven navigation. Items without href render as <button> elements, so keyboard activation (Enter / Space) works without extra wiring.

Mobile / narrow viewports#

By default, the bar collapses to a top-row hamburger below md (768px) and the items move into a left-side Drawer. Both orientations share the same drawer body so users on small screens get the vertical list regardless of which orientation the desktop layout uses. Pass responsive={false} if your app handles its own responsive behavior.

Top-level props#

Props for NavBar
PropTypeDefaultDescription
orientationenumhorizontalLayout direction. Default `'horizontal'`.
items *NavBarItem[]Item tree driving the bar.
brandReactNodeBrand / logo slot rendered at the start. When `responsive` is `true`, `brand` also seeds the mobile Drawer's accessible name, so it should include text — e.g. `<><Logo /> ShipIt</>` rather than `<Logo />` alone. Falls back to `'Navigation'` when omitted.
actionsReactNodeTrailing slot for secondary actions (avatar, settings, theme toggle, …).
valuestring | undefinedControlled active item id.
defaultValuestring | undefinedUncontrolled initial active item id.
onValueChange((id: string) => void) | undefinedFired when an item is activated.
widthnumber | undefined240Pixel width of the vertical rail. Default 240.
responsiveboolean | undefinedtrueCollapse to a hamburger drawer below `md`. Default `true`.