27.7k

Navbar

A composable top navigation bar with hide-on-scroll, responsive mobile menu, and client-side routing support

Import

import { Navbar } from '@darkcode-ui/react';

Usage

"use client";

import {Button, Navbar} from "@darkcode-ui/react";
import {Sparkles} from "@gravity-ui/icons";

Anatomy

Import the Navbar component and access all parts using dot notation.

import { Navbar } from '@darkcode-ui/react';

export default () => (
  <Navbar>
    <Navbar.Header>
      <Navbar.Brand />
      <Navbar.MenuToggle />
      <Navbar.Content>
        <Navbar.Item />
        <Navbar.Label />
        <Navbar.Separator />
        <Navbar.Spacer />
      </Navbar.Content>
    </Navbar.Header>
    <Navbar.Menu>
      <Navbar.MenuItem />
    </Navbar.Menu>
  </Navbar>
)

Docs Site

A docs-site style navbar with a search bar, theme segment, and responsive mobile menu.

"use client";

import {Navbar, SearchField, ToggleButton, ToggleButtonGroup} from "@darkcode-ui/react";
import {Display, Moon, Sparkles, Sun} from "@gravity-ui/icons";

With Dropdowns

SaaS-style navbar with a team switcher, user dropdown, and mobile menu.

"use client";

import {
  Avatar,
  Button,

Dashboard

Breadcrumb-style dashboard navbar built with InlineSelect for the workspace, project, and timezone selectors.

"use client";

import {Button, InlineSelect, ListBox, Navbar} from "@darkcode-ui/react";
import {Bell, ChevronRight, Sparkles} from "@gravity-ui/icons";

Compact

A dense navbar tuned via --spacing and height overrides for a Luma-style compact layout.

"use client";

import type {CSSProperties} from "react";

import {Button, Navbar} from "@darkcode-ui/react";

With Menu

Responsive navbar with a mobile hamburger toggle and animated dropdown menu.

"use client";

import {Button, Navbar} from "@darkcode-ui/react";
import {Sparkles} from "@gravity-ui/icons";

Hide on Scroll

Pass hideOnScroll to hide the navbar when the user scrolls down and reveal it on scroll up.

"use client";

import {Button, Navbar} from "@darkcode-ui/react";
import {Sparkles} from "@gravity-ui/icons";
import {useRef} from "react";

Centered

Use justify on Navbar.Content to lay out the bar as brand · centered nav · actions without manual spacers. justify="center" lets the group grow and center, while justify="end" pushes the actions to the far edge.

"use client";

import {Button, Navbar} from "@darkcode-ui/react";
import {Sparkles} from "@gravity-ui/icons";

Responsive Mobile Menu

Navbar.MenuToggle and Navbar.Menu provide a built-in mobile dropdown. Use hidden md:flex on desktop-only groups and keep the toggle visible below the breakpoint:

<Navbar>
  <Navbar.Header>
    <Navbar.MenuToggle className="md:hidden" />
    <Navbar.Brand>…</Navbar.Brand>

    {/* Desktop-only nav */}
    <Navbar.Content className="hidden md:flex">
      <Navbar.Item href="/features">Features</Navbar.Item>
      <Navbar.Item href="/pricing">Pricing</Navbar.Item>
    </Navbar.Content>
  </Navbar.Header>

  {/* Mobile-only menu — animates in when the toggle is pressed */}
  <Navbar.Menu>
    <Navbar.MenuItem href="/features">Features</Navbar.MenuItem>
    <Navbar.MenuItem href="/pricing">Pricing</Navbar.MenuItem>
  </Navbar.Menu>
</Navbar>

The menu open state can be controlled via isMenuOpen / onMenuOpenChange on Navbar, or uncontrolled via defaultMenuOpen. By default the navbar locks body scroll while the menu is open — pass shouldBlockScroll={false} to disable.

Compact Density

Scale the entire navbar (paddings, gaps, icon sizes) by overriding the Tailwind v4 --spacing variable on the navbar or a wrapper, and tune the bar height with the height prop:

<Navbar
  height="2.75rem"
  size="sm"
  style={{"--spacing": "0.22rem"} as React.CSSProperties}
>
  …
</Navbar>
  • --spacing scales every Tailwind spacing utility (gap-*, p-*, size-*) inside the navbar
  • height overrides the internal --navbar-height CSS variable
  • size="sm" applies the built-in compact text + padding variants

Client-Side Routing

Navbar.Item renders a real <a> element when href is set (and Navbar.MenuItem does the same for the mobile menu), so it works with any router. You have two ways to integrate client-side routing.

Pass your router's push function to Navbar. Every Navbar.Item / Navbar.MenuItem with an href will route through it, preventing full-page reloads.

"use client";

import { useRouter } from "next/navigation";
import { Navbar } from "@darkcode-ui/react";

export function AppNavbar() {
  const router = useRouter();

  return (
    <Navbar navigate={router.push}>
      <Navbar.Header>
        <Navbar.Brand>Acme</Navbar.Brand>
        <Navbar.Content>
          <Navbar.Item href="/features">Features</Navbar.Item>
          <Navbar.Item href="/pricing">Pricing</Navbar.Item>
        </Navbar.Content>
      </Navbar.Header>
    </Navbar>
  );
}

Since Navbar.Item and Navbar.MenuItem are polymorphic, you can swap the rendered element entirely to use a framework-specific <Link> component (preserving prefetch, scroll restoration, and any router-level behaviors):

import Link from "next/link";
import { Navbar } from "@darkcode-ui/react";

export function AppNavbar() {
  return (
    <Navbar>
      <Navbar.Header>
        <Navbar.Content>
          <Navbar.Item
            href="/features"
            render={(props) => <Link href="/features" {...props} />}
          >
            Features
          </Navbar.Item>
        </Navbar.Content>
      </Navbar.Header>
    </Navbar>
  );
}

External URLs (starting with http:// or https://) are automatically opened in a new tab via window.open(href, "_blank", "noopener,noreferrer"). For internal links that need a full page reload instead of client-side navigation, use the forceReload prop:

<Navbar.Item href="https://github.com/DarkCode-Developers/darkcode-ui">GitHub</Navbar.Item>

<Navbar.Item href="/legacy-page" forceReload>Legacy page</Navbar.Item>

If no navigate function is provided, href falls back to the browser's default navigation.

Positioning

Use the position prop to control how the navbar is anchored:

  • "sticky" (default) — sticks to the top of its scroll container (sticky top-0)
  • "static" — flows inline with the page
  • "floating" — sticky, but detached from the edges with rounded corners, margin, border, and a shadow
<Navbar position="floating" maxWidth="xl">…</Navbar>

Appearance

  • isBlurred (default true) — a frosted-glass backdrop: a translucent background plus backdrop-blur. Pass isBlurred={false} for a solid bar.
  • isBordered — adds a bottom border.
  • Elevate on scroll — once the scroll container has moved, the navbar gains a subtle shadow (data-scrolled="true"). Floating navbars keep their own shadow instead.
<Navbar isBordered isBlurred>…</Navbar>
<Navbar isBlurred={false}>…</Navbar>

Scroll Behavior

  • onScrollPositionChange(position) — called with the scroll offset as the container scrolls.
  • disableScrollHandler — skip the internal scroll listener entirely (no hide / elevate / callback).
  • disableAnimation — opt out of the menu, toggle, and hide-on-scroll transitions.
<Navbar hideOnScroll onScrollPositionChange={(y) => console.log(y)}>…</Navbar>

Programmatic Control

Use the useNavbar hook inside children of Navbar for programmatic access to the menu state and scroll-hidden state:

import { useNavbar } from "@darkcode-ui/react";

function CloseMenuButton() {
  const { isMenuOpen, setMenuOpen } = useNavbar();

  return (
    <Button isDisabled={!isMenuOpen} onPress={() => setMenuOpen(false)}>
      Close menu
    </Button>
  );
}

Styling

Customizing the component classes

To customize the Navbar component classes, you can use the @layer components directive.
Learn more.

@layer components {
  .navbar {
    --navbar-height: 3.5rem;
  }

  .navbar__item {
    @apply font-semibold;
  }
}

DarkCode UI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The Navbar component uses these CSS classes (View source styles):

Base & Variant Classes

  • .navbar — Root <nav> element. Flex column, bg-background, z-40.
  • .navbar--sticky — Sticky to the top of the scroll container.
  • .navbar--static — Inline positioning.
  • .navbar--floating — Sticky, detached with rounded-2xl, border, shadow-surface, and mx-2.
  • .navbar--bordered — Adds a bottom border.
  • .navbar--blurred — Translucent background + backdrop-blur (applied by default; falls back to a solid background when backdrop-filter is unsupported).
  • .navbar--no-animation — Disables transitions/animations.
  • .navbar[data-hidden="true"] — Applied while the navbar is hidden via hideOnScroll.
  • .navbar[data-scrolled="true"] — Applied once the scroll container has scrolled (adds an elevate shadow).
  • .navbar[data-menu-open="true"] — Applied while the mobile menu is open.

Layout Classes

  • .navbar__header — Horizontal bar wrapper, clamped by --navbar-max-width and --navbar-height.
  • .navbar__header--max-sm|md|lg|xl|2xl|full — Sets --navbar-max-width.
  • .navbar__header--sm|lg — Size variants that tune header padding.
  • .navbar__brand — Logo / site-title container.
  • .navbar__content — Horizontal group of items.
  • .navbar__content--center|end — Justify the content group (via the justify prop).
  • .navbar__spacer — Flex-grow gap that pushes sibling content to the edges.

Item Classes

  • .navbar__item — Nav link/button.
  • .navbar__item--sm|lg — Size variants for items.
  • .navbar__item[data-current="true"] — Applied when isCurrent is true. Also sets aria-current="page".
  • .navbar__label — Truncated text inside items.
  • .navbar__separator — Vertical decorative divider.
  • .navbar__menu-toggle — Hamburger button. Morphs between hamburger and close icon.
  • .navbar__menu-toggle--sm|lg — Size variants.
  • .navbar__menu-toggle-icon — Inner span housing the animated lines.
  • .navbar__menu — Mobile dropdown menu, fills calc(100dvh - var(--navbar-height)). Carries data-state="open"|"closed" to drive enter/exit animations.
  • .navbar__menu-item — Individual mobile menu item.
  • .navbar__menu-item--sm|lg — Size variants.
  • .navbar__menu-item[data-current="true"] — Current mobile menu item.

CSS Variables

  • --navbar-height — Height of the horizontal bar (default 4rem; 3rem for sm; 5rem for lg). Can also be set via the height prop.
  • --navbar-max-width — Max content width inside the header (default 1024px, overridable via maxWidth).
  • --navbar-transition-duration — Hide-on-scroll transition duration (default 300ms).
  • --spacing — Tailwind v4 base spacing token. Override on the navbar root to uniformly scale internal paddings, gaps, and icon sizes.

API Reference

The root <nav> landmark and the context provider. Accepts all <nav> props.

PropTypeDefaultDescription
position"sticky" | "static" | "floating""sticky"How the navbar is anchored to the page.
size"sm" | "md" | "lg""md"Applies the matching size variants to the header, items, and menu toggle.
maxWidth"sm" | "md" | "lg" | "xl" | "2xl" | "full""lg"Caps the header's content width via --navbar-max-width.
isBlurredbooleantrueFrosted-glass backdrop (translucent background + backdrop-blur).
isBorderedbooleanfalseAdd a bottom border.
heightstring"4rem"CSS height for the bar. Sets --navbar-height inline.
hideOnScrollbooleanfalseHide the navbar when scrolling down and reveal on scroll up.
disableScrollHandlerbooleanfalseSkip the internal scroll listener (no hide / elevate / position callback).
onScrollPositionChange(position: number) => void—Called with the scroll offset whenever the scroll container scrolls.
disableAnimationbooleanfalseOpt out of menu, toggle, and hide-on-scroll transitions.
parentRefRefObject<HTMLElement | null>windowScroll container used by hideOnScroll / scroll callbacks. Defaults to the window.
isMenuOpenboolean—Controlled mobile menu open state.
defaultMenuOpenbooleanfalseInitial mobile menu open state (uncontrolled).
onMenuOpenChange(isOpen: boolean) => void—Callback when the mobile menu state changes.
shouldBlockScrollbooleantrueLock body scroll while the mobile menu is open.
navigate(href: string) => void—Programmatic navigation function used by Navbar.Item / Navbar.MenuItem.

Horizontal bar wrapper. Renders a <header> with the header size and max-width slot classes applied.

Logo / site-title container. Polymorphic — accepts a render prop to customize the rendered element. Renders a <div> by default.

Horizontal group of nav items. Renders a <div>.

PropTypeDefaultDescription
justify"start" | "center" | "end""start"Align the group within the header. center grows and centers; end pushes the group to the far edge.

A single nav link or action. Renders an <a> when href is set, otherwise a <button>. Polymorphic via the render prop.

PropTypeDefaultDescription
hrefstring—Destination URL. Uses the parent Navbar's navigate for client-side routing, or the browser's default navigation as a fallback. External URLs open in a new tab.
isCurrentbooleanfalseMarks the item as the current page. Sets aria-current="page" and data-current="true".
isActivebooleanfalseAlias for isCurrent.
forceReloadbooleanfalseSkip the navigate callback and use window.location.href for this item.
render(props) => ReactElement—Override the rendered element.

Truncated text span for use inside Navbar.Item.

Vertical divider. Wraps the DarkCode UI Separator with orientation="vertical".

Flex-grow spacer that pushes surrounding content to opposite ends of the header.

Hamburger button for the mobile menu. Wraps RAC ToggleButton; its selected state is bound to isMenuOpen.

PropTypeDefaultDescription
srLabelstring"Toggle navigation menu"Screen-reader label for the toggle.
childrenReactNode—Custom icon. When omitted, the built-in animated hamburger/close icon is used.

Animated mobile dropdown. Only renders when isMenuOpen is true. Fills the viewport below the navbar with calc(100dvh - var(--navbar-height)).

An individual item inside Navbar.Menu. Same API surface as Navbar.Item — but pressing an item automatically closes the mobile menu.

useNavbar

A hook for reading and mutating navbar state from inside Navbar's children.

const { height, isHidden, isScrolled, isMenuOpen, setMenuOpen, navigate } = useNavbar();
PropertyTypeDescription
heightstringCurrent navbar height (as passed via the height prop).
isHiddenbooleanWhether the navbar is currently hidden via hideOnScroll.
isScrolledbooleanWhether the scroll container has scrolled past the top.
isMenuOpenbooleanWhether the mobile menu is currently open.
setMenuOpen(open: boolean) => voidProgrammatically open/close the mobile menu.
navigate((href: string) => void) | undefinedThe navigation function passed to Navbar, if any.

Accessibility

  • Rendered as a <nav> landmark. Add an aria-label when multiple navigation regions share the page.
  • Navbar.Item with isCurrent (or isActive) sets aria-current="page" automatically.
  • Navbar.MenuToggle is backed by RAC ToggleButton; its selected state is wired to isMenuOpen. Customize the screen-reader label via the srLabel prop.
  • While the mobile menu is open, focus is contained within it, the first item is focused on open, and focus is restored to the toggle on close.
  • The mobile menu closes on Escape and auto-closes when the viewport grows to the desktop breakpoint.
  • Animations respect the user's prefers-reduced-motion preference, and can be disabled entirely with disableAnimation.

On this page