# Topbar

> A React top bar for app headers: menu button, title or breadcrumb, a search in the middle and actions at the end, on a sticky glass bar.

- Type: React component (Navigation)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add topbar`
- Docs: https://wingo-ui.com/components/topbar

## Interaction

Scroll the page and a hairline fades in under the glass bar. On a phone the bar slides away as you scroll down, following your finger, and comes straight back the moment you scroll up, at the top and at the end of the page; tabbing into a hidden bar springs it back, and it never hides while focus is inside it. Inside an app shell the menu button opens the drawer on phones (the two lines cross into an X) and collapses the sidebar on desktop, with a tooltip that shows the shell's shortcut (Cmd/Ctrl+B by default). Tab moves through the menu button, the title links, the search and the actions. On phones the title is centered on the screen and the search folds into an icon next to the actions.

## Installation

```bash
npx wingo-ui@latest add topbar
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/topbar` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/topbar.tsx`

npm dependencies: `npm i motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Tooltip](https://wingo-ui.com/components/tooltip) ([Markdown](https://wingo-ui.com/components/tooltip.md))
- [useMediaQuery](https://wingo-ui.com/components/use-media-query) ([Markdown](https://wingo-ui.com/components/use-media-query.md))
- [useScrollState](https://wingo-ui.com/components/use-scroll-state) ([Markdown](https://wingo-ui.com/components/use-scroll-state.md))
- [useShortcut](https://wingo-ui.com/components/use-shortcut) ([Markdown](https://wingo-ui.com/components/use-shortcut.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
"use client"

import { useState } from "react"
import { Bell, Search } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Topbar } from "@/components/ui/topbar"

export function ClientsHeader() {
  const [menuOpen, setMenuOpen] = useState(false)
  return (
    <Topbar
      title="Clients"
      titleAs="h1"
      menuButton="always"
      menuOpen={menuOpen}
      onMenuClick={() => setMenuOpen((open) => !open)}
      actions={
        <>
          <Button variant="ghost" size="icon" radius="full" aria-label="Search">
            <Search />
          </Button>
          <Button variant="ghost" size="icon" radius="full" aria-label="Notifications">
            <Bell />
          </Button>
        </>
      }
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title?` | `ReactNode` | `""` | Text or a breadcrumb. Truncates when there is no room; with no title the bar holds start and actions only. |
| `mobileTitle?` | `ReactNode` |  | A shorter title below 768px, usually the breadcrumb's last entry. |
| `titleAs?` | `"div" \| "h1" \| "h2" \| "span"` | `"div"` | The title element. Use h1 only when the page has no other. |
| `start?` | `ReactNode` |  | After the menu button: a logo, a back button, a workspace switcher. |
| `center?` | `ReactNode` |  | The middle: a global search, a CommandMenuTrigger, a segmented switch. Below 768px it moves next to the actions, so give it a compact phone form (both do: an icon). |
| `actions?` | `ReactNode` |  | The end: notifications, the user menu, buttons. Icon buttons: ghost, size icon, radius full. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Bar height 48 / 56 / 64px plus the top safe area; the title grows with it. |
| `background?` | `"blur" \| "solid" \| "transparent"` | `"blur"` | blur: frosted glass over the page. solid: the page background. transparent: nothing until content scrolls under it, then glass (a header over a hero photo). The bar reads --bar-bg for the surface it sits on. |
| `border?` | `"scrolled" \| "always" \| "never"` | `"scrolled"` | The bottom hairline: fades in once content scrolls under the bar, always there, or never. |
| `mobileTitleAlign?` | `"start" \| "center"` | `"center"` | Below 768px. center puts the title in the middle of the screen like iOS, not between uneven buttons. |
| `sticky?` | `boolean` | `true` | Pins the bar to the top of its scroll container (sticky top-0). Off, it scrolls away with the page and never hides. |
| `safeArea?` | `boolean` | `true` | Pads env(safe-area-inset-top) for notched phones. It is 0 in desktop browsers, so the default is harmless. |
| `hideOnScroll?` | `"never" \| "mobile" \| "always"` | `"mobile"` | Slides the bar away while scrolling down, following the finger, and brings it back on scroll up; mobile does it below 768px only. Snaps after scrolling stops; under reduced motion it flips instead of sliding. |
| `menuButton?` | `"auto" \| "always" \| "never"` | `"auto"` | auto: inside an app shell (useNavShell) the hamburger on phones and the sidebar toggle on desktop; outside one, nothing. always: also outside a shell, a hamburger that calls onMenuClick. never: no menu button. For custom headers use TopbarMenuButton (see Parts), the same toggle alone. |
| `menuShortcut?` | `string` | `"mod+b"` | The sidebar shortcut shown in the toggle's tooltip and set as aria-keyshortcuts. Display only: the shell binds it. "" hides it. |
| `menuOpen?` | `boolean` | `false` | Outside a shell: your drawer is open. The hamburger turns into an X and reports aria-expanded. In the playground it applies with menuButton="always", which drops the demo's app shell. |
| `onMenuClick?` | `() => void` |  | Called by the menu button. Outside a shell it is the only thing the button does (open your own drawer). |
| `menuControls?` | `string` | `""` | The id of the drawer or sidebar the menu button controls, set as aria-controls. |
| `scrollTarget?` | `HTMLElement \| Window \| RefObject<HTMLElement \| null> \| null` |  | What scrolls, for the hairline and hide on scroll. Default: the nearest ScrollContainerProvider, else the window. |
| `labels?` | `{ openMenu?: string; closeMenu?: string; collapse?: string; expand?: string }` |  | The menu button's aria-labels and tooltip. Default: the shell's labels, else "Open menu", "Close menu", "Collapse sidebar", "Expand sidebar". |
| `classNames?` | `Partial<Record<"inner" \| "start" \| "title" \| "center" \| "actions" \| "menuButton", string>>` |  | Extra classes for the inner parts. |
| `className?` | `string` |  | Extra classes for the <header>, merged last. The bar sets data-scrolled, so text-white data-scrolled:text-foreground styles a header over a photo. Set [--bar-bg:var(--surface)] on a card. |

## Parts

### TopbarMenuButton (component)

The hamburger / sidebar toggle on its own, for custom headers. Inside an app shell it drives the shell; outside one it calls onClick. Takes every Button prop except children and asChild (variant ghost, size icon and radius full by default).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `kind?` | `"auto" \| "hamburger" \| "sidebar"` | `"auto"` | auto: inside a shell a hamburger on phones and the sidebar toggle on desktop, outside one a hamburger. hamburger and sidebar force the look. |
| `open?` | `boolean` |  | Outside a shell: the drawer it opens is open, so the hamburger turns into an X. |
| `controls?` | `string` |  | The id of the drawer or sidebar it controls, for aria-controls. |
| `shortcut?` | `string` | `"mod+b"` | The sidebar shortcut shown in the tooltip and set as aria-keyshortcuts. Display only; the shell binds it. |
| `tooltip?` | `boolean` | `true` | The tooltip on the desktop sidebar toggle. |
| `labels?` | `TopbarLabels` |  | openMenu, closeMenu, collapse and expand aria-labels. |

### TopbarLabels (type)

The labels object: { openMenu?, closeMenu?, collapse?, expand? }, shared by Topbar and TopbarMenuButton.

## Examples

- **Minimal**: A title and one action; the hairline appears once the list scrolls.
- **Transparent over a photo**: background="transparent" until content scrolls under it, then glass; data-scrolled flips the text color.
- **Phone detail page**: Centered title, a back button in start, and the bar sliding away as you scroll down.
- **Large, with tabs under it**: size="lg"; the tabs stick at var(--topbar-offset) and ride up as the bar hides.
- **Command menu in the middle**: The search pattern: a field on desktop, an icon on phones.
- **Solid with a border**: background="solid" and border="always" for forms and settings.
- **Your own drawer**: Outside an app shell, menuButton="always" + onMenuClick; the lines cross into an X while it is open.

## See also

- [App Shell](https://wingo-ui.com/components/app-shell) ([Markdown](https://wingo-ui.com/components/app-shell.md))
- [Sidebar](https://wingo-ui.com/components/sidebar) ([Markdown](https://wingo-ui.com/components/sidebar.md))
- [Breadcrumb](https://wingo-ui.com/components/breadcrumb) ([Markdown](https://wingo-ui.com/components/breadcrumb.md))
- [Large Title Header](https://wingo-ui.com/components/large-title-header) ([Markdown](https://wingo-ui.com/components/large-title-header.md))
- [Bottom Tab Bar](https://wingo-ui.com/components/bottom-tab-bar) ([Markdown](https://wingo-ui.com/components/bottom-tab-bar.md))
- [useScrollState](https://wingo-ui.com/components/use-scroll-state) ([Markdown](https://wingo-ui.com/components/use-scroll-state.md))

---

Canonical: https://wingo-ui.com/components/topbar
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
