suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
GitHubGet started
DocsComponentsPatternsInteractionsChartsBlocks
Patterns19
  • Overview
  • Action Bar
  • Action Menu
  • App Shell
  • Command Palette
  • Confirm
  • Context Actions
  • Date Picker
  • Flyout
  • Modal
  • Navbar
  • Paged List
  • Panel
  • Picker
  • Preview
  • Search Picker
  • Segment Picker
  • Split View
  • Table List
  • Trail

suiss/ui · Pattern

Navbar

A site header that renders a navigation menu on wide screens and a fullscreen menu when space runs out.

Navbar is an open-source pattern from suiss/ui for React. The @esuiss/ui CLI copies its source into your project, so you own the code.

import {
  BookOpenIcon,
  ChartLineIcon,
  CodeIcon,
  LifebuoyIcon,
  LightningIcon,
  ShieldCheckIcon,
} from "@phosphor-icons/react"
import { Navbar, type NavbarItem } from "@/components/patterns/navbar"

const items: NavbarItem[] = [
  {
    label: "Products",
    items: [
      {
        label: "Analytics",
        href: "#analytics",
        description: "Track traffic and conversions in real time.",
        icon: (
          <ChartLineIcon className="mt-0.5" />
        ),
      },
      {
        label: "Automations",
        href: "#automations",
        description: "Run workflows when events happen.",
        icon: (
          <LightningIcon className="mt-0.5" />
        ),
      },
      {
        label: "Security",
        href: "#security",
        description: "Single sign-on, audit logs, and roles.",
        icon: (
          <ShieldCheckIcon className="mt-0.5" />
        ),
      },
    ],
  },
  {
    label: "Resources",
    items: [
      {
        label: "Documentation",
        href: "#docs",
        icon: (
          <BookOpenIcon />
        ),
      },
      {
        label: "API reference",
        href: "#api",
        icon: (
          <CodeIcon />
        ),
      },
      {
        label: "Help center",
        href: "#help",
        icon: (
          <LifebuoyIcon />
        ),
      },
    ],
  },
  { label: "Pricing", href: "#pricing" },
  { label: "Blog", href: "#blog" },
]

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarDemo() {
  return (
    <div className="min-h-96">
      <Navbar items={items} brand={brand} />
    </div>
  )
}

Navbar builds a header from an items array. On wide screens it renders a NavigationMenu: plain links appear inline, groups open a dropdown of links with optional icons and descriptions, and mega items open multi-column panels. The brand sits at the start and actions at the end.

The navbar switches to a compact layout below the 48rem breakpoint, and also on wider screens whenever its contents overflow the header; it measures itself with a ResizeObserver and returns to the full menu once there is room again. In the compact layout the items move into a FullscreenMenu opened by a menu button, with plain links in the first group and each group or mega column as a labelled section. Use it as the primary navigation of a marketing site or documentation site.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/navbar

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add patterns navbar

Manual

  1. Add the cn helper.

  2. Add the pieces it builds on: Fullscreen Menu, Navigation Menu, use-mobile.

  3. Copy the source into components/patterns/navbar.tsx:

components/patterns/navbar.tsx
"use client"

import * as React from "react"
import {
  FullscreenMenu,
  FullscreenMenuContent,
  FullscreenMenuGroup,
  FullscreenMenuLabel,
  FullscreenMenuLink,
  FullscreenMenuTitle,
  FullscreenMenuTrigger,
} from "@/components/ui/fullscreen-menu"
import {
  NavigationMenu,
  NavigationMenuContent,
  NavigationMenuItem,
  1. Update the import paths to match your project.

Usage

import { Navbar } from "@/components/patterns/navbar"
<div className="min-h-96">
  <Navbar items={items} brand={brand} />
</div>

Each NavbarItem is one of three shapes: a link with label and href (plus optional description, icon and active), a group with label and items, or a mega item with label and columns. Each column has a label, links and an optional featured flag that sets its links in a large type size. Pass render to route links through your framework's link component; it receives href and the link content.

Examples

With actions

Adds a Sign in button at the end of the header with actions.

import {
  BookOpenIcon,
  ChartLineIcon,
  CodeIcon,
  LifebuoyIcon,
  LightningIcon,
  ShieldCheckIcon,
} from "@phosphor-icons/react"
import { Navbar, type NavbarItem } from "@/components/patterns/navbar"
import { Button } from "@/components/ui/button"

const items: NavbarItem[] = [
  {
    label: "Products",
    items: [
      {
        label: "Analytics",
        href: "#analytics",
        description: "Track traffic and conversions in real time.",
        icon: (
          <ChartLineIcon className="mt-0.5" />
        ),
      },
      {
        label: "Automations",
        href: "#automations",
        description: "Run workflows when events happen.",
        icon: (
          <LightningIcon className="mt-0.5" />
        ),
      },
      {
        label: "Security",
        href: "#security",
        description: "Single sign-on, audit logs, and roles.",
        icon: (
          <ShieldCheckIcon className="mt-0.5" />
        ),
      },
    ],
  },
  {
    label: "Resources",
    items: [
      {
        label: "Documentation",
        href: "#docs",
        icon: (
          <BookOpenIcon />
        ),
      },
      {
        label: "API reference",
        href: "#api",
        icon: (
          <CodeIcon />
        ),
      },
      {
        label: "Help center",
        href: "#help",
        icon: (
          <LifebuoyIcon />
        ),
      },
    ],
  },
  { label: "Pricing", href: "#pricing" },
  { label: "Blog", href: "#blog" },
]

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarWithActions() {
  return (
    <div className="min-h-96">
      <Navbar
        items={items}
        brand={brand}
        actions={
          <Button size="sm" variant="secondary">
            Sign in
          </Button>
        }
      />
    </div>
  )
}

Links only

Uses only plain links and marks the current page with active.

import { Navbar } from "@/components/patterns/navbar"

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarLinksOnly() {
  return (
    <div className="min-h-96">
      <Navbar
        items={[
          { label: "Overview", href: "#overview", active: true },
          { label: "Customers", href: "#customers" },
          { label: "Changelog", href: "#changelog" },
          { label: "Blog", href: "#blog" },
        ]}
        brand={brand}
      />
    </div>
  )
}

Crowded

Shows nine links in a narrow frame, folding them into the menu until the frame is wide enough.

import { Navbar } from "@/components/patterns/navbar"
import { Button } from "@/components/ui/button"

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarCrowded() {
  return (
    <div className="min-h-96">
      <div data-testid="frame" className="w-160 max-w-full">
        <Navbar
          items={[
            "Store",
            "Laptop",
            "Tablet",
            "Phone",
            "Watch",
            "Headphones",
            "Speakers",
            "Accessories",
            "Support",
          ].map((label) => ({ label, href: `#${label.toLowerCase()}` }))}
          brand={brand}
          actions={
            <Button size="sm" variant="secondary">
              Sign in
            </Button>
          }
        />
      </div>
    </div>
  )
}

With close label

Labels the fullscreen menu's close button Done with closeLabel.

import {
  BookOpenIcon,
  ChartLineIcon,
  CodeIcon,
  LifebuoyIcon,
  LightningIcon,
  ShieldCheckIcon,
} from "@phosphor-icons/react"
import { Navbar, type NavbarItem } from "@/components/patterns/navbar"

const items: NavbarItem[] = [
  {
    label: "Products",
    items: [
      {
        label: "Analytics",
        href: "#analytics",
        description: "Track traffic and conversions in real time.",
        icon: (
          <ChartLineIcon className="mt-0.5" />
        ),
      },
      {
        label: "Automations",
        href: "#automations",
        description: "Run workflows when events happen.",
        icon: (
          <LightningIcon className="mt-0.5" />
        ),
      },
      {
        label: "Security",
        href: "#security",
        description: "Single sign-on, audit logs, and roles.",
        icon: (
          <ShieldCheckIcon className="mt-0.5" />
        ),
      },
    ],
  },
  {
    label: "Resources",
    items: [
      {
        label: "Documentation",
        href: "#docs",
        icon: (
          <BookOpenIcon />
        ),
      },
      {
        label: "API reference",
        href: "#api",
        icon: (
          <CodeIcon />
        ),
      },
      {
        label: "Help center",
        href: "#help",
        icon: (
          <LifebuoyIcon />
        ),
      },
    ],
  },
  { label: "Pricing", href: "#pricing" },
  { label: "Blog", href: "#blog" },
]

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarWithCloseLabel() {
  return (
    <div className="min-h-96">
      <Navbar items={items} brand={brand} closeLabel="Done" />
    </div>
  )
}

Mega panel

Uses layout="panel" with a mega item whose columns open in a full-width panel.

import { Navbar } from "@/components/patterns/navbar"

const brand = <span className="text-base font-semibold">Acme</span>

export default function NavbarMegaPanel() {
  return (
    <div className="min-h-96">
      <Navbar
        items={[
          {
            label: "Products",
            columns: [
              {
                label: "Explore",
                featured: true,
                links: [
                  { label: "Analytics", href: "#analytics" },
                  { label: "Automations", href: "#automations" },
                  { label: "Security", href: "#security" },
                ],
              },
              {
                label: "Get started",
                links: [
                  { label: "Pricing", href: "#pricing" },
                  { label: "Customers", href: "#customers" },
                ],
              },
              {
                label: "Learn",
                links: [
                  { label: "Documentation", href: "#docs" },
                  { label: "Help center", href: "#help" },
                ],
              },
            ],
          },
          { label: "Pricing", href: "#pricing" },
          { label: "Blog", href: "#blog" },
        ]}
        brand={brand}
        layout="panel"
      />
    </div>
  )
}

API Reference

01

Navbar

The header that renders the brand, navigation items and actions.

PropTypeDefaultDescription
itemsNavbarItem[]RequiredThe navigation items: links, groups of links, or mega items with columns.
brandReact.ReactNode-Content shown at the start of the header, such as a logo.
actionsReact.ReactNode-Content shown at the end of the header, such as a sign-in button; it stays visible in the compact layout.
menuLabelstring"Menu"Accessible label of the compact menu button and title of the fullscreen menu.
closeLabelstring-Text for the fullscreen menu's close button.
layout"popover" | "panel""popover"With popover, dropdowns float beneath their trigger; with panel, they open as a full-width panel anchored to the header.
renderReact.ReactElement<NavbarAnchor />The element used for every link, defaulting to a plain anchor; replace it with your router's link component.

Also accepts the props of React.ComponentProps<"header">.

Accessibility

  • Group and mega triggers are buttons with aria-expanded reflecting their state, and Escape closes an open dropdown.
  • Links marked active have aria-current="page".
  • In the compact layout, the menu button is labelled by menuLabel.

Notes

The header carries data-slot="navbar", data-layout, and data-compact while the fullscreen menu is in use. The brand and actions wrappers use data-slot="navbar-brand" and data-slot="navbar-actions", and mega columns use data-slot="navbar-column" with data-featured. With layout="panel" the header gains relative positioning and z-50 so the panel can anchor to it.

NextPaged ListPreviousModal

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • With actions
    • Links only
    • Crowded
    • With close label
    • Mega panel
  4. 04API Reference
    • Navbar
  5. 05Accessibility
  6. 06Notes