Viana Kitv0.1.4

Blocks

Header

A composable application header bar with layout slots for a title, search input group, and right-side actions.

example.tsx
Dashboard
KA

Import

tsx
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.

PropTypeDefaultDescription
classNamestringAdditional classes merged onto the header element.
childrenReactNodeSlot 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.

PropTypeDefaultDescription
classNamestringAdditional classes merged onto the content div.
childrenReactNodeHeader 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.

PropTypeDefaultDescription
childrenReactNodeTitle text content.
classNamestringAdditional 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.

PropTypeDefaultDescription
childrenReactNodeTypically an AppButtonGroup containing AppInput + AppButton.
classNamestringAdditional 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.

PropTypeDefaultDescription
childrenReactNodeAction controls — AppSelect, AppButton, AppAvatar, or any combination.
classNamestringAdditional 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.

tsx
<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.

tsx
<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.

tsx
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.

no-title.tsx
KA

Source

src/components/blocks/AppHeader.tsx
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,
}