Blocks
Header
A composable application header bar with layout slots for a title, search input group, and right-side actions.
Import
import {
AppHeader,
AppHeaderContent,
AppHeaderTitle,
AppHeaderSearchbar,
AppHeaderActions,
} from "@/components/blocks/AppHeader"API Reference
AppHeader
The outer <header> element. Height is controlled by the --header-height CSS variable, which should be set on the layout root alongside --sidebar-width. Accepts all standard <header> HTML attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Additional classes merged onto the header element. |
| children | ReactNode | — | Slot content — typically AppHeaderContent. |
AppHeaderContent
Inner flex container. Provides horizontal padding (px-4 on mobile, px-6 on large screens) and a consistent gap between children.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Additional classes merged onto the content div. |
| children | ReactNode | — | Header slots: AppHeaderTitle, AppHeaderSearchbar, AppHeaderActions. |
AppHeaderTitle
Optional page or section label. Renders as an inline <span> with shrink-0 so it never compresses when the searchbar expands. Omit it when no title is needed.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Title text content. |
| className | string | — | Additional classes merged onto the span. |
AppHeaderSearchbar
A flex-1 layout slot that expands to fill available space between the title and actions. Place an AppButtonGroup inside to compose input and button combinations. The slot imposes no opinions on the input group's internal layout.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Typically an AppButtonGroup containing AppInput + AppButton. |
| className | string | — | Additional classes merged onto the searchbar div. |
AppHeaderActions
Right-side slot. Uses ml-auto to push itself to the far right of the header. The default composition is AppSelect + AppAvatar, but any components can be placed inside in any order.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Action controls — AppSelect, AppButton, AppAvatar, or any combination. |
| className | string | — | Additional classes merged onto the actions div. |
Usage
With sidebar layout
Place AppHeader as the first child of AppSidebarInset. Set --header-height on the AppSidebarProvider style alongside --sidebar-width.
<AppSidebarProvider
style={{
"--sidebar-width": "calc(var(--spacing) * 72)",
"--header-height": "calc(var(--spacing) * 14)",
} as React.CSSProperties}
>
<AppSidebar>...</AppSidebar>
<AppSidebarInset>
<AppHeader>
<AppHeaderContent>
<AppSidebarTrigger />
<AppHeaderSearchbar>
<AppButtonGroup>
<AppInput placeholder="Search..." />
<AppButton variant="default"><Search className="size-4" /></AppButton>
</AppButtonGroup>
</AppHeaderSearchbar>
<AppHeaderActions>
<AppSelect>...</AppSelect>
<AppAvatar><AppAvatarFallback>KA</AppAvatarFallback></AppAvatar>
</AppHeaderActions>
</AppHeaderContent>
</AppHeader>
<main>Page content</main>
</AppSidebarInset>
</AppSidebarProvider>With optional title
Add AppHeaderTitle between the sidebar trigger and the searchbar. Omit it on pages where the current context is already obvious.
<AppHeaderContent>
<AppSidebarTrigger />
<AppHeaderTitle>Sites</AppHeaderTitle>
<AppHeaderSearchbar>...</AppHeaderSearchbar>
<AppHeaderActions>...</AppHeaderActions>
</AppHeaderContent>Inserting extra controls in AppHeaderActions
Place any component between or alongside the defaults — order is fully controlled by the consumer.
import { LayoutGrid, Bell } from "lucide-react"
<AppHeaderActions>
<AppSelect>...</AppSelect>
<AppButton variant="ghost" size="icon">
<LayoutGrid className="size-4" />
</AppButton>
<AppButton variant="ghost" size="icon">
<Bell className="size-4" />
</AppButton>
<AppAvatar>
<AppAvatarImage src="/avatar.png" alt="Kevin" />
<AppAvatarFallback>KA</AppAvatarFallback>
</AppAvatar>
</AppHeaderActions>Searchbar without title
Omit AppHeaderTitle when the current page context is already clear. The searchbar expands to fill the available space.
Source
import * as React from "react"
import { cn } from "../../lib/utils"
function AppHeader({ className, ...props }: React.HTMLAttributes<HTMLElement>) {
return (
<header
className={cn(
"dark flex h-(--header-height) shrink-0 items-center gap-2 border-b border-header-border bg-header text-header-foreground transition-[width,height] ease-linear",
className
)}
{...props}
/>
)
}
function AppHeaderContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("flex w-full items-center gap-2 px-4 lg:px-6", className)}
{...props}
/>
)
}
function AppHeaderTitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span
className={cn("shrink-0 text-sm font-medium text-header-foreground", className)}
{...props}
/>
)
}
function AppHeaderSearchbar({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("flex flex-1 items-center", className)}
{...props}
/>
)
}
function AppHeaderActions({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("ml-auto flex items-center gap-2", className)}
{...props}
/>
)
}
export {
AppHeader,
AppHeaderContent,
AppHeaderTitle,
AppHeaderSearchbar,
AppHeaderActions,
}