A bar with a wordmark, a Services trigger, plain links and two buttons. Services drops a compact list where items with a chevron open a second-level flyout to their right on hover, click or Arrow Right, and Arrow Left steps back. On phones a panel slides down under the bar with a two-level accordion. Both levels and the mobile panel stay in the markup and are toggled with CSS.
A bar with a wordmark, a Services trigger, plain links and two buttons. Services drops a compact list whose chevron items open a second-level flyout to the right, driven by hover, click or the arrow keys, and on phones a panel slides down with a two-level accordion.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://beste.dev/r/navbar94?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://beste.dev/r-base/navbar94?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/navbar94.tsx plus the shadcn button primitive it uses.
The installed file exports navbar94Demo alongside the block: the exact props behind the preview above. Spread it to get a working navbar in one line.
import { Navbar94, navbar94Demo } from "@/components/beste/block/navbar94";
export default function Page() {
return <Navbar94 {...navbar94Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Navbar94 } from "@/components/beste/block/navbar94";
export default function Page() {
return (
<Navbar94
brand="Meridian"
services={{
label: "Services",
items: [
{
label: "Strategy",
href: "/strategy",
description: "Where to play and how to win there.",
children: [
{ label: "Market research", href: "/strategy/research" },
{ label: "Positioning", href: "/strategy/positioning" },
],
},
{ label: "Workshops", href: "/workshops" },
],
}}
links={[
{ label: "Work", href: "/work" },
{ label: "About", href: "/about" },
]}
buttons={[
{ label: "Contact sales", href: "/contact", variant: "ghost" },
{ label: "Book a call", href: "/book" },
]}
labels={{ overview: "Overview", open: "Open menu", close: "Close menu" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
brand | string | – | Wordmark on the left |
services | ServicesMenu | – | Trigger label and the first-level items; items with children get a flyout |
links | NavItem[] | [] | Plain links after the trigger |
buttons | ActionButton[] | [] | Buttons on the right, stacked full width in the mobile panel |
labels | { overview?: string; open?: string; close?: string } | {} | Label of the link to the parent page in each flyout, and accessible names for the menu toggle |
className | string | – | Extra classes for the outer <header> |
type ServicesMenu = {
label: string;
items: ServiceItem[];
};
type ServiceItem = {
label: string;
href: string;
description?: string;
children?: NavItem[];
};
type ActionButton = {
label: string;
href: string;
variant?: "default" | "outline" | "ghost" | "secondary";
};
type NavItem = {
label: string;
href: string;
};aria-haspopup, aria-expanded and aria-controls. Arrow Right, Enter or Space opens the flyout and focuses its first link; Arrow Left inside the flyout closes it and returns focus to the parent row.grid-rows-[0fr] to grid-rows-[1fr], the panel is inert while closed, and any link inside it closes it. A ghost button becomes an outline button in the panel so it still reads as a button.