Content Header
Breadcrumbs, a title, actions, and docked tabs that stay in sync as you move between screens.
<ContentHeader breadcrumbs> + <Tabs>content-headerbreadcrumbstabs9:41
In this demo
Bramble — project tracker
What to try
- Click a breadcrumb to jump between screens
- Switch tabs — the row stays docked under the header
Code
app.tsx
import { AppShell, Header, Content, ContentHeader, Breadcrumbs, BreadcrumbItem, Tabs, Tab } from "appshell-react";
import { useState } from "react";
export default function App() {
// Breadcrumb clicks just move between in-page "screens" — no router needed.
const [screen, setScreen] = useState<"workspace" | "projects" | "project">("project");
const [tab, setTab] = useState("tasks");
return (
<AppShell safeArea>
<Header behavior="sticky" logo={<span className="font-bold">Bramble</span>} />
<Content className="mx-auto max-w-2xl">
<ContentHeader
breadcrumbs={
<Breadcrumbs>
<BreadcrumbItem label="Bramble" onClick={() => setScreen("workspace")} current={screen === "workspace"} />
{screen !== "workspace" && (
<BreadcrumbItem label="Projects" onClick={() => setScreen("projects")} current={screen === "projects"} />
)}
{screen === "project" && <BreadcrumbItem label="Website redesign" current />}
</Breadcrumbs>
}
title="Website redesign"
subtitle="12 open tasks · due Oct 3"
actions={<button className="rounded-lg bg-primary px-3 py-2 text-sm font-medium text-primary-foreground">New task</button>}
/>
{/* Tabs dock below the Header via --header-height automatically. */}
<Tabs value={tab} onValueChange={setTab}>
<Tab value="tasks" label="Tasks" />
<Tab value="milestones" label="Milestones" />
<Tab value="files" label="Files" />
</Tabs>
{tab === "tasks" && <TaskList />}
</Content>
</AppShell>
);
}