App header
The bar at the top of a signed-in app: brand on the left, a few nav
links, then search, notifications, and an account menu on the right.
Nav items render as real <a> elements (not buttons) so browser
navigation, middle-click, and “open in new tab” all work. Swap brand
for your own mark, and set activeIndex to underline the current
section.
'use client';
import type { ReactNode } from 'react';import { Avatar, Button, Flex, IconButton, Menu, MenuItem, MenuSeparator, MenuTrigger, Text,} from '@helix-ui/core';import { Bell, HelixIcon, Search } from '@helix-ui/icons';
export interface AppHeaderProps { /** Brand mark. Defaults to the helix-ui glyph + wordmark. */ brand?: ReactNode; links: { label: string; href: string }[]; /** Index of the active link, for the current-page treatment. */ activeIndex?: number; user: { name: string; initials: string; avatarUrl?: string }; onSearch?: () => void; onSignOut?: () => void;}
export function AppHeader({ brand, links, activeIndex, user, onSearch, onSignOut,}: AppHeaderProps) { return ( <Flex align="center" justify="between" gap={4} style={{ height: 56, padding: '0 var(--helix-ui-space-5)', borderBottom: '1px solid var(--helix-ui-color-border-default)', background: 'var(--helix-ui-color-bg-surface-default)', }} > <Flex align="center" gap={6}> {brand ?? ( <Flex align="center" gap={2}> <HelixIcon /> <Text weight="semibold">helix-ui</Text> </Flex> )} <nav style={{ display: 'flex', gap: 'var(--helix-ui-space-1)' }}> {links.map((link, i) => ( <a key={link.href} href={link.href} aria-current={i === activeIndex ? 'page' : undefined} style={{ padding: 'var(--helix-ui-space-2) var(--helix-ui-space-3)', borderRadius: 'var(--helix-ui-radius-md)', fontSize: 'var(--helix-ui-font-size-sm)', textDecoration: 'none', color: i === activeIndex ? 'var(--helix-ui-color-text-primary)' : 'var(--helix-ui-color-text-secondary)', }} > {link.label} </a> ))} </nav> </Flex>
<Flex align="center" gap={1}> <IconButton aria-label="Search" onClick={onSearch}> <Search /> </IconButton> <IconButton aria-label="Notifications"> <Bell /> </IconButton> <MenuTrigger> <Button variant="ghost" tone="neutral" aria-label={`Account: ${user.name}`} style={{ padding: 'var(--helix-ui-space-1)' }} > <Avatar size="sm" fallback={user.initials} src={user.avatarUrl} alt={user.name} /> </Button> <Menu> <MenuItem>Profile</MenuItem> <MenuItem>Settings</MenuItem> <MenuSeparator /> <MenuItem destructive onAction={onSignOut}> Sign out </MenuItem> </Menu> </MenuTrigger> </Flex> </Flex> );}Usage
<AppHeader links={[ { label: 'Home', href: '/' }, { label: 'Projects', href: '/projects' }, { label: 'Reports', href: '/reports' }, ]} activeIndex={1} user={{ name: 'Sara Tang', initials: 'ST' }} onSearch={openCommandPalette} onSignOut={signOut}/>Notes
- The search button is a good place to open a
CommandPalette(⌘K) rather than expanding an inline field — it keeps the bar compact. - The account trigger is a
Buttonwrapping anAvatar, so it stays keyboard-focusable and opens theMenuon Enter/Space.