KQ-1301 · APP/SHELL
App Shell
The frame a product's screens live in: a sidebar whose width is one motion value on glide, so its labels tuck away row by row and leave a 56px icon rail with tooltips, its edge dragged 1:1 and committed by projection with the release velocity; below 640px it becomes a drawer that slides over a scrim on glide and can be pushed back out. A pill under the current row travels on snap, pages change by slide, fade or zoom with the last crumb cross-fading in the same beat, and search is a combobox over the pages. Arrow keys walk the sidebar, [ or Cmd/Ctrl+B folds it, 1–9 change page and / goes to search.
Overview
Tuesday · seven jobs across three teams
Completed this week
76 jobsRecent activity
- 10:52Team North is 20 minutes late to 3 Quarry Lane
- 10:41Mira Chen set off for Basin Yard 2
- 10:05J-4102 closed with a signed sheet
- 09:12Tomas Reyes arrived at Larch Court
- 08:30Seven jobs dispatched for today
overview · seven jobs, three teams · 1 to 3 switch pages · [ folds the sidebar
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/app-shell
Props
- pages
- AppShellPage[]
- default defaultShellPages
- The destinations in sidebar order, each with its label, icon, optional badge and section, crumbs, heading, summary, search keywords and content.
- page / defaultPage
- string
- default the first page
- The current page's id. Controlled or uncontrolled.
- onPageChange
- (id: string) => void
- default —
- Fires from the row, crumb, shortcut or search result that chose a page.
- collapsed / defaultCollapsed
- boolean
- default open from 1024px, folded below
- The sidebar folded to its rail (or hidden without rail). Controlled or uncontrolled; left unset it follows the shell's width.
- onCollapsedChange
- (collapsed: boolean) => void
- default —
- Fires from the toggle, the edge drag, the edge's arrow keys or the shortcut, with the new fold.
- rail
- boolean
- default true
- Fold to a 56px icon rail whose labels become tooltips. Off: the sidebar slides away entirely and the content takes the width.
- transition
- "slide" | "fade" | "zoom"
- default "slide"
- How the content changes page: rising 16px in the direction of travel through the nav, a cross-fade, or settling from 0.98 scale.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Row heights (28/32/36px), the top bar (44/48/56px), paddings and the heading size.
- workspace
- AppShellWorkspace
- default defaultShellWorkspace
- The product at the top of the sidebar: name, detail line and an optional 28px mark.
- user
- AppShellUser
- default defaultShellUser
- Who is signed in, at the foot of the sidebar.
- navLabel
- string
- default "Main"
- The sidebar navigation's accessible name.
- searchPlaceholder
- string
- default "Search " + workspace name
- The search field's placeholder and accessible name.
- onSearch
- (query: string) => void
- default —
- Enter in search when no page matches, with the query.
- shortcuts
- boolean
- default true
- [ or Cmd/Ctrl+B folds the sidebar, 1–9 go to a page and / focuses search, while focus is in the shell or it is hovered with nothing focused; never while typing.
- status
- "ready" | "loading" | "error"
- default "ready"
- The content area's state: a skeleton while loading, a message with Retry on error. The sidebar keeps working.
- onRetry
- () => void
- default —
- The error state's Retry button.
- label
- string
- default workspace name
- The shell's accessible name.
- sound
- boolean
- default false
- A swish as the visitor folds or opens the sidebar or drawer, a click pitched by place as they choose a page.
- className
- string
- default —
- Classes for the root. It is 560px tall by default; pass a height (h-full, h-dvh) to fill a screen.
| Prop | Type | Default | Description |
|---|---|---|---|
| pages | AppShellPage[] | defaultShellPages | The destinations in sidebar order, each with its label, icon, optional badge and section, crumbs, heading, summary, search keywords and content. |
| page / defaultPage | string | the first page | The current page's id. Controlled or uncontrolled. |
| onPageChange | (id: string) => void | — | Fires from the row, crumb, shortcut or search result that chose a page. |
| collapsed / defaultCollapsed | boolean | open from 1024px, folded below | The sidebar folded to its rail (or hidden without rail). Controlled or uncontrolled; left unset it follows the shell's width. |
| onCollapsedChange | (collapsed: boolean) => void | — | Fires from the toggle, the edge drag, the edge's arrow keys or the shortcut, with the new fold. |
| rail | boolean | true | Fold to a 56px icon rail whose labels become tooltips. Off: the sidebar slides away entirely and the content takes the width. |
| transition | "slide" | "fade" | "zoom" | "slide" | How the content changes page: rising 16px in the direction of travel through the nav, a cross-fade, or settling from 0.98 scale. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Row heights (28/32/36px), the top bar (44/48/56px), paddings and the heading size. |
| workspace | AppShellWorkspace | defaultShellWorkspace | The product at the top of the sidebar: name, detail line and an optional 28px mark. |
| user | AppShellUser | defaultShellUser | Who is signed in, at the foot of the sidebar. |
| navLabel | string | "Main" | The sidebar navigation's accessible name. |
| searchPlaceholder | string | "Search " + workspace name | The search field's placeholder and accessible name. |
| onSearch | (query: string) => void | — | Enter in search when no page matches, with the query. |
| shortcuts | boolean | true | [ or Cmd/Ctrl+B folds the sidebar, 1–9 go to a page and / focuses search, while focus is in the shell or it is hovered with nothing focused; never while typing. |
| status | "ready" | "loading" | "error" | "ready" | The content area's state: a skeleton while loading, a message with Retry on error. The sidebar keeps working. |
| onRetry | () => void | — | The error state's Retry button. |
| label | string | workspace name | The shell's accessible name. |
| sound | boolean | false | A swish as the visitor folds or opens the sidebar or drawer, a click pitched by place as they choose a page. |
| className | string | — | Classes for the root. It is 560px tall by default; pass a height (h-full, h-dvh) to fill a screen. |