suiss/ui · Pattern
App Shell
An application frame with a collapsible sidebar on desktop and a floating tab bar on mobile.
App Shell is an open-source pattern from suiss/ui for React. The @esuiss/ui CLI copies its source into your project, so you own the code.
AppShell builds navigation from an items array of AppShellItem objects and wraps your page content. On desktop it renders a Sidebar inside a SidebarProvider, with optional header and footer, group labels, badges, tooltips on collapsed icons and a SidebarRail; the page renders in a SidebarInset.
Below the 48rem breakpoint the sidebar is replaced by a TabBar pinned to the bottom of the screen, and the content renders in a main element padded to clear it. Items that do not fit in the tab bar move behind its More trigger. Use it as the outermost layout of an app with a handful of top-level destinations.
Installation
CLI
Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:
Manual
-
Add the
cnhelper. -
Copy the source into
components/patterns/app-shell.tsx:
- Update the import paths to match your project.
Usage
Each AppShellItem has a value, label and icon, plus optional href, badge, group and disabled. Items with the same group render under a shared SidebarGroupLabel. The selected item is controlled with value and onValueChange, or uncontrolled with defaultValue; it defaults to the first item. Items with an href render through render, which defaults to a plain anchor, so you can pass your router's link component.
The sidebar accepts variant (sidebar, floating or inset), collapsible (icon by default, offcanvas or none), side, rail, and open, defaultOpen and onOpenChange for its expanded state. moreLabel sets the tab bar's More trigger text and label names the navigation landmark (Main by default). Place AppShellTrigger in your page header to toggle the sidebar.
Examples
With header and footer
Adds a brand menu in the sidebar header and an account menu in the footer.
Grouped
Splits items into labelled Platform and Workspace groups and disables one item.
More tab
Uses eight items with defaultValue so extra items overflow into the mobile tab bar's More panel.
Links
Gives every item an href so each renders as a link to its route.
Floating
Uses the floating sidebar variant.
Inset
Uses the inset sidebar variant.
Offcanvas
Sets collapsible to offcanvas so collapsing slides the whole sidebar out of view.
Right side
Places the sidebar on the right with side.
Collapsed
Starts with the sidebar collapsed to icons through defaultOpen={false}.
API Reference
AppShell
The root layout that renders the sidebar or tab bar from items and wraps the page content.
| Prop | Type | Default | Description |
|---|---|---|---|
items | AppShellItem[] | Required | The destinations, each with a value, label and icon. |
value | string | - | The selected item, for controlled use. |
defaultValue | string | First item | The initially selected item when uncontrolled. |
onValueChange | (value: string) => void | - | Called when an item is chosen. |
header | React.ReactNode | - | Content at the top of the sidebar. |
footer | React.ReactNode | - | Content at the bottom of the sidebar. |
variant | "sidebar" | "floating" | "inset" | "sidebar" | The sidebar style. |
collapsible | "offcanvas" | "icon" | "none" | "icon" | How the sidebar collapses. |
side | "left" | "right" | "left" | The side the sidebar sits on. |
rail | boolean | true | Shows a rail along the sidebar edge that toggles it. |
open | boolean | - | Whether the sidebar is expanded, for controlled use. |
defaultOpen | boolean | true | Whether the sidebar starts expanded when uncontrolled. |
onOpenChange | (open: boolean) => void | - | Called when the sidebar expands or collapses. |
moreLabel | string | "More" | Text of the More tab on mobile. |
render | React.ReactElement | <AppShellLink /> | The element items with an href render as, such as your router's link. |
Also accepts the props of React.ComponentProps<typeof AppShellLayout>.
AppShellTrigger
A SidebarTrigger that toggles the sidebar on desktop and renders nothing on mobile.
Also accepts the props of React.ComponentProps<typeof SidebarTrigger>.
Accessibility
- The selected item has
aria-current="page". - Collapsed sidebar items show their label in a tooltip on hover.
AppShellTriggeris labelled Toggle Sidebar, andCmd+Bon macOS orCtrl+Belsewhere toggles the sidebar.- With
collapsible="offcanvas", the hidden sidebar is removed from view and the tab order. - Disabled items are disabled buttons.
Notes
The sidebar exposes data-state (expanded or collapsed), data-collapsible, data-variant and data-side for styling. On mobile the wrapper carries data-slot="app-shell" and the content data-slot="app-shell-content"; className applies to the content area on both layouts. rail only renders when collapsible is not none.