The bar across the top of an app: your brand, the main links and a few controls, with the current page marked aria-current="page", and the link of the section it is in aria-current="true", both underlined in the primary colour as in FamilyBar, distinct from hover and press highlights. A group of links ({ label, links }) opens a panel, as in NavigationMenu. Whenever the links do not fit, below 640px and on a narrow tablet, they collapse behind a menu button into a Sheet that slides in from the side, with each group under its name; the brand and actions stay in the bar. Links render through OuroProvider's linkComponent, so your router navigates.
On this site
This site's own pages use Navbar too. Open one with X-ray pointing it out: The Nautilus's story · The home page
In Spoonjoy
Spoonjoy, a recipe app, is built with Ouro HX (how).
See it live, no sign-in needed: Home · A recipe · Recipes
In its source: app/root.tsx
Install
Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):
npx shadcn@latest init --base base --preset nova --yes --no-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.3.10Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):
npx shadcn@latest add ourostack/ouro-ui/navbar#v1.3.10Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/navbarIts tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):
npx shadcn@latest add ourostack/ouro-ui/navbar#v1.3.10 ourostack/ouro-ui/navbar-tests#v1.3.10import { Navbar } from "@/components/ouro/navbar/navbar";Paths use shadcn's default @/ alias; the CLI rewrites them to yours.
Example
<Navbar
label="Main"
brand={<Link href="/">Spoonjoy</Link>}
items={[
{ label: "Recipes", href: "/recipes" },
{ label: "Cookbooks", href: "/cookbooks" },
]}
currentPath={pathname}
actions={<Button href="/recipes/new">New recipe</Button>}
/>Usage
The phone menu opens once JavaScript has loaded (after hydration); the links are in the server HTML from 640px up. A tap on the menu button before then does nothing unless your app keeps it: this site's head script keeps a tap on any control React has not yet hydrated, shows the control pressed, and presses it again once it has (site/src/early-input.ts in the repository). Once the menu button shows, the sheet is built while the page is idle and kept, hidden, between opens, so a press only shows it.
Use NavigationMenu alone for a row of panels inside a page, and Tabs for views of one page. StackedLayout puts a Navbar above the page.
Use instead when
- Use
Sidebarinstead for more than a handful of destinations, or sections of them.
Props
Type: NavbarProps.
actions- Type:
ReactNode - Controls at the end of the bar, at every width: a search button, an account
Menu. brand- Type:
ReactNode - Your logo or product name, at the start of the bar. Link it to the home page.
className- Type:
string - Layout only: margins, position (
sticky top-0). Appearance classes warn in development. closeLabel- Type:
string - Default:
"Close" - The label of that sheet's close button.
currentPath- Type:
string - The path of the page being shown, from your router (
useLocation().pathname, orusePathname()in Next.js). The link to that path getsaria-current="page"and the current look. On a deeper page, the longest link above it, the section (/recipesfor/recipes/12), gets the same look andaria-current="true", so a screen reader hears one current page. items(required)- Type:
readonly NavbarItem[] - The links, in order, and groups of links (
{ label, links }), which open a panel in the bar. When they do not fit, as on a phone, they move into a sheet. label(required)- Type:
string - Names the navigation for screen readers ("Main"). It is not shown.
menu- Type:
ReactNode - What the sheet behind the menu button holds in place of the
itemslinks, when a phone needs more than the bar, such as a section's own pages under its name, or a filter over a long list. It replaces them, so list the links they held too. It sits in the sheet'snav; mark the current page yourself (aria-current="page"). Choosing a link in it closes the sheet. Once the page has been idle it mounts, hidden, ahead of the first press (a press before then mounts it as it opens), and it mounts afresh the same way after each close, so each open starts with the state it first had. menuLabel- Type:
string - Default:
"Menu" - The name of the button that opens the links below 640px, and the title of the sheet it opens.
ref- Type:
Ref<HTMLElement> - Reaches the rendered element.
Don't use
These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.
children: Not rendered. The bar isitems,brandandactions.links: Not a prop. Ouro UI usesitems:<Navbar label="Main" items={[{ label: "Recipes", href: "/recipes" }]} />.logo: Not a prop. Ouro UI usesbrand:<Navbar brand={<Logo />}>.pathname: Not a prop. Ouro UI usescurrentPath:<Navbar currentPath={pathname}>.
className is for layout only
Pass margins, width and grid placement (mt-4, w-full, col-span-2).
Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.
Alignment classes (text-center) warn too: set alignment on a parent element.
Types
NavbarGroup
A group of links in a Navbar: a button that opens a panel in the bar, and a labelled group in the sheet.
interface NavbarGroup {
/** The group's name, the button's text: "Recipes". */
label: string;
/**
* The group's links, each with an optional `description`.
*/
links: readonly NavbarLink[];
/** Not with `links`. To link the section's own page, make it the group's first link. */
href?: never;
}NavbarItem
One item of a Navbar: a link (href), or a group of links (links).
type NavbarItem = NavbarLink | NavbarGroup;NavbarLink
One link in a Navbar: in the bar, or in a group's panel.
interface NavbarLink {
/** The link's text: "Recipes". */
label: string;
/** Where it goes. Internal paths go through `OuroProvider`'s `linkComponent` (your router's link). */
href: string;
/**
* In a group: a line under the label in the bar's panel, "Everything you have saved."
*/
description?: string | undefined;
/**
* Marks this link as the current page (`aria-current="page"`), when the Navbar's
* `currentPath` cannot tell: a link whose page lives at another path.
*/
current?: boolean | undefined;
/** Not with `href`. An item is a link (`href`) or a group of links (`links`). */
links?: never;
}