Navbar
betaThe bar across the top of a site: brand, links, and actions, with a menu on narrow screens.
Preview
flat links, with the current page marked
floating: a pill once the page scrolls
no actions
narrow container: press the menu button
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the website component. It also adds every component it uses. Its own files go in your
componentsfolder, and they are yours to edit.npx shadcn@latest add @txbrendan/navbar
Use it for
- The top of a marketing site, docs site, or app, with up to six main links.
- Group related pages into a menu, with `items`, like Product or Who it's for. Give its items a short `description` when the names alone don't say enough.
- Mark the page you are on with `current: true`, so it gets a soft fill and aria-current.
- Pass your router's link through `renderLink`, like next/link, so links stay client-side.
- Put a sign-in and a main call to action in `actions`. On narrow screens they move into the menu.
- Set `collapseBelow` to the smallest width your links and actions fit in. More links need a bigger one.
- Set `floating` on a landing page, so the bar stays in reach as a pill while people scroll.
Avoid it for
- More than six entries in the bar. Group the rest into a menu, or move them into the footer.
- A menu with one item, or more than about eight. Link straight to the one page, or split the list.
- A menu inside a menu. On narrow screens each menu is a heading with its links under it, one level deep.
- Links inside an app's own screens, like settings sections. Use Tabs, or a sidebar.
- A trail of where you are. Use Breadcrumb.
Props
- brand
Your logo or name. It links to brandHref.
ReactNode
- brandHref
Where the brand links.
string · default "/"
- links
The main links, in order. Each href must be unique, since it is the key. A menu, with `items`, opens a list of links instead.
({ href: string; label: string; current?: boolean } | NavbarMenu)[]
- NavbarMenu
Type. A menu in the bar. It opens on hover after a short pause, or on a click or Enter. Items with a description show it under the label, two columns from four items. An icon needs a description. Labels must be unique.
{ label: string; items: { href: string; label: string; description?: ReactNode; icon?: ReactNode; current?: boolean }[] }
- actions
Buttons on the right, like Sign in and Create a team. On narrow screens they sit at the bottom of the menu.
ReactNode
- renderLink
Draws the brand and each link. Spread the props onto your router's link: (props) => <Link {...props} />.
(props: NavbarLinkProps) => ReactNode · default a plain <a>
- label
The name screen readers give the nav.
string · default "Main"
- menuLabel
The name of the menu button on narrow screens.
string · default "Menu"
- collapseBelow
The navbar's own width below which links fold into the menu: 36, 42, 48, or 56rem. Pick the smallest your links and actions fit in.
"sm" | "md" | "lg" | "xl" · default "lg"
- floating
Pins the navbar to the top. Once the page scrolls, the bar lifts into a rounded pill with a shadow, and the menu opens as a rounded panel.
boolean · default false
- className
Extra classes on the header, which holds the container query. Add sticky top-0 z-40 here to pin it without the pill.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| brand | ReactNode | - | Your logo or name. It links to brandHref. |
| brandHref | string | "/" | Where the brand links. |
| links | ({ href: string; label: string; current?: boolean } | NavbarMenu)[] | - | The main links, in order. Each href must be unique, since it is the key. A menu, with `items`, opens a list of links instead. |
| NavbarMenu | { label: string; items: { href: string; label: string; description?: ReactNode; icon?: ReactNode; current?: boolean }[] } | - | Type. A menu in the bar. It opens on hover after a short pause, or on a click or Enter. Items with a description show it under the label, two columns from four items. An icon needs a description. Labels must be unique. |
| actions | ReactNode | - | Buttons on the right, like Sign in and Create a team. On narrow screens they sit at the bottom of the menu. |
| renderLink | (props: NavbarLinkProps) => ReactNode | a plain <a> | Draws the brand and each link. Spread the props onto your router's link: (props) => <Link {...props} />. |
| label | string | "Main" | The name screen readers give the nav. |
| menuLabel | string | "Menu" | The name of the menu button on narrow screens. |
| collapseBelow | "sm" | "md" | "lg" | "xl" | "lg" | The navbar's own width below which links fold into the menu: 36, 42, 48, or 56rem. Pick the smallest your links and actions fit in. |
| floating | boolean | false | Pins the navbar to the top. Once the page scrolls, the bar lifts into a rounded pill with a shadow, and the menu opens as a rounded panel. |
| className | string | - | Extra classes on the header, which holds the container query. Add sticky top-0 z-40 here to pin it without the pill. |
Accessibility
- The links sit in a nav landmark named by `label`. Only one copy of the links is on screen at a time, so screen readers hear one nav.
- The current link has aria-current="page".
- On narrow screens a menu button opens the links. It says whether it is open with aria-expanded.
- Escape closes the menu and puts focus back on its button. A press outside the bar closes it too.
- Links in the menu are at least 44px tall for touch.
- On narrow screens `actions` render a second time, inside the menu. Do not give them an id.
- A menu's trigger is a button with aria-expanded. Its list opens as a non-modal popup named by the menu's label, so Tab moves on past it and closes it.
- Escape closes a menu and puts focus back on its trigger. Picking an item closes it too.
- The trigger also opens on hover, but never only on hover: a click, Enter, or Space opens it as well.
- In the narrow menu each menu's links sit in a list named by its heading, so nothing nests.
Examples
A marketing site
<Navbar
brand={<span className="font-semibold">Workspace</span>}
links={[
{ href: "/features", label: "Features" },
{ href: "/pricing", label: "Pricing", current: true },
{ href: "/docs", label: "Docs" },
]}
actions={
<>
<Button emphasis="ghost" size="sm">Sign in</Button>
<Button tone="brand" size="sm">Create a team</Button>
</>
}
/>A floating pill on a landing page
<Navbar
floating
brand="Workspace"
links={[{ href: "/features", label: "Features" }, { href: "/pricing", label: "Pricing" }]}
actions={<Button tone="brand" emphasis="outline" size="sm">Request access</Button>}
/>With menus
<Navbar
brand="Workspace"
links={[
{
label: "Product",
items: [
{ href: "/plan", label: "Planning", description: "Set up the week, with who is coming." },
{ href: "/agendas", label: "Agendas", description: "Run saved agendas and track goals." },
],
},
{ href: "/pricing", label: "Pricing" },
{ label: "More", items: [{ href: "/blog", label: "Blog" }, { href: "/about", label: "About" }] },
]}
/>With next/link, pinned to the top
<Navbar
className="sticky top-0 z-40 border-b"
brand="Workspace"
links={[{ href: "/", label: "Home", current: true }, { href: "/blog", label: "Blog" }]}
renderLink={(props) => <Link {...props} />}
/>