Components
Tabs
Sticky sub-navigation, solved once: a tab row that docks below the Header through the --header-height variable the Header publishes, with real tablist semantics and arrow-key navigation.
Docked below the header
Because the Header keeps --header-height up to date via a ResizeObserver, the row stays glued below it whatever rows the header shows — no hand-tuned offsets. Selection is controlled (value/onValueChange) or uncontrolled (defaultValue, falling back to the first tab); you render the matching panel from the current value.
9:41
profile.tsx
import { AppShell, Header, Content, Tabs, Tab } from "appshell-react";
import { useState } from "react";
export default function Profile() {
const [tab, setTab] = useState("posts");
return (
<AppShell safeArea>
<Header behavior="fixed" title="Nadia Reyes" subtitle="@nadia" />
{/* Docks itself below the fixed header — no manual top offset */}
<Tabs value={tab} onValueChange={setTab} aria-label="Profile sections">
<Tab value="posts" label="Posts" />
<Tab value="replies" label="Replies" badge={<Badge>3</Badge>} />
<Tab value="media" label="Media" />
</Tabs>
<Content>
{tab === "posts" && <Posts />}
{tab === "replies" && <Replies />}
{tab === "media" && <MediaGrid />}
</Content>
</AppShell>
);
}Accessibility
The row is a real role="tablist": tabs carry aria-selected, only the active one is in the Tab order, and ArrowLeft/ArrowRight move and select with wrap-around at the ends.
Pass
sticky={false} for an inline, non-docking row — segmented filters inside content, for example.API
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | — | Controlled selected value. |
| defaultValue | string | — | Uncontrolled initial value. Defaults to the first Tab. |
| onValueChange | (value: string) => void | — | Selection handler. |
| sticky | boolean | true | Dock below the Header via --header-height. false renders an inline row. |
| className | string | — | Extra classes for the row. |
| aria-label | string | "Tabs" | Accessible tablist name. |
| children* | ReactNode | — | Tab elements. |
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | — | Identifies this tab in value/onValueChange. |
| label* | string | — | Tab text. |
| badge | ReactNode | — | Trailing count or dot. |
| className | string | — | Extra classes. |