<SiteHeader.Root>
<SiteHeader.Bar>
<SiteHeader.Brand href="/"><BrandLogo /></SiteHeader.Brand>
<SiteHeader.Nav>
<SiteHeader.NavList>
<SiteHeader.NavItem href="/#pricing">Pricing</SiteHeader.NavItem>
</SiteHeader.NavList>
<SiteHeader.Actions>
<Button size="sm">I'm hiring</Button>
</SiteHeader.Actions>
</SiteHeader.Nav>
<SiteHeader.MobileToggle aria-label="Open or close the menu" />
</SiteHeader.Bar>
<SiteHeader.MobileMenu>
<SiteHeader.MobileNavItem href="/#pricing">Pricing</SiteHeader.MobileNavItem>
</SiteHeader.MobileMenu>
</SiteHeader.Root>MobileToggle and MobileMenu share their open state through the root, so the
two lists are declared separately but only one is ever visible.
The mobile menu
Below the breakpoint the menu is a full-screen overlay under the bar: its list
scrolls on its own, MobileActions pins its buttons to the bottom (clear of the
home indicator), and following a MobileNavItem closes it. While it is open the
page behind is frozen through useScrollLock — import
@voila.dev/ui/styles/landing.css once, which turns the flag into
overflow: hidden and releases it past the breakpoint on its own.
collapseBelow="xl" keeps the burger until xl, for a header with more links
and buttons than a tablet can hold:
<SiteHeader.Root collapseBelow="xl">…</SiteHeader.Root>open/onOpenChange control the menu, so it can be mutually exclusive with
another full-screen surface such as a cart drawer — which can lock the page with
the same hook:
const [cartOpen, setCartOpen] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
useScrollLock(cartOpen);
<SiteHeader.Root
open={menuOpen}
onOpenChange={(next) => {
setMenuOpen(next);
if (next) setCartOpen(false);
}}
>API
SiteHeader.Root
| Prop | Type | Default | |
|---|---|---|---|
collapseBelow | SiteHeaderCollapse | "md" | The width under which the burger menu replaces the desktop nav. A header
with more links and buttons than md can hold collapses until xl
instead of wrapping. |
defaultOpen | boolean | false | Whether the mobile menu starts open. Leave it off: the menu server-renders closed and hydrates, so opening by default flashes on first paint. |
onOpenChange | ((open: boolean) => void) | — | |
open | boolean | — | Controlled open state — pair with onOpenChange. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <header> prop. Source: landing/components/site-header/root.tsx.
SiteHeader.Actions
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <div> prop. Source: landing/components/site-header/actions.tsx.
SiteHeader.Bar
| Prop | Type | Default | |
|---|---|---|---|
aria-label | string | — | Defines a string value that labels the current element. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <nav> prop. Source: landing/components/site-header/bar.tsx.
SiteHeader.Brand
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <a> prop. Source: landing/components/site-header/brand.tsx.
SiteHeader.MobileActions
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <li> prop. Source: landing/components/site-header/mobile-actions.tsx.
SiteHeader.MobileMenu
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <div> prop. Source: landing/components/site-header/mobile-menu.tsx.
SiteHeader.MobileNavItem
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <a> prop. Source: landing/components/site-header/mobile-nav-item.tsx.
SiteHeader.MobileToggle
| Prop | Type | Default | |
|---|---|---|---|
aria-label | string | required | Defines a string value that labels the current element. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <button> prop. Source: landing/components/site-header/mobile-toggle.tsx.
SiteHeader.Nav
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <div> prop. Source: landing/components/site-header/nav.tsx.
SiteHeader.NavItem
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <a> prop. Source: landing/components/site-header/nav-item.tsx.
SiteHeader.NavList
| Prop | Type | Default | |
|---|---|---|---|
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
render | ComponentRenderFn<HTMLProps, {}> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
Plus every <ul> prop. Source: landing/components/site-header/nav-list.tsx.