AppShell
StableThe outer frame of a screen: a narrow side panel and one main area.
Also known as layout, app layout, sidebar layout, scaffold
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Side panel and main area
<div className="h-105 overflow-hidden rounded-card border border-line">
<AppShell rail={<ConversationList items={railItems} activeId="1" onSelect={() => {}} onNew={() => {}} />}>
<div className="p-6 text-body leading-5 text-fg-muted">Main content goes here.</div>
</AppShell>
</div>The frame fills the space around it, so give that space a height. Make the window narrow and the side panel disappears.
When to use it
The frame around every screen: a narrow side panel for navigation and one main area for the work. The side panel stays narrow so it never competes with the main area.
Made of: Side panel · Main area
Use it for
- The outer frame of any app screen.
- A screen with past chats or navigation in a side panel.
- A full-width screen with no side panel: leave rail out.
Not for
- Splitting the main area into chat and work side by side: use SplitCanvasTemplate.
- Spacing content inside a section: use Stack.
Do and avoid
Chats
Trips
Lisbon trip
Chats
Trips
Lisbon trip
Do
- Put navigation and the user card in the side panel so the main area stays calm.
- Give the frame a space with a set height.
- Put a template (HomeTemplate, ThreadTemplate) inside it.
- Check how it looks without the side panel, since it is hidden on small screens.
Avoid
- Do not put important actions only in the side panel. It is hidden on narrow screens.
- Do not add a third column. Use SplitCanvasTemplate inside the main area instead.
- Do not put an AppShell inside another AppShell.
On a phone
- Below the md breakpoint the sidebar moves into a side sheet that opens from a Menu button above the main area. Use menuLabel to rename that button.
- The main area takes the full width and can shrink without overflowing.
- The shell is as tall as its parent, so give the parent a height; use the dynamic viewport height so phone browser bars are counted.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| rail | ReactNode | none | Left sidebar content such as a conversation list and user card. Rendered in an aside with 272px width from md up. Below md it opens from a Menu button in a left side sheet. |
| menuLabel | string | Menu | Name of the button that opens the rail on a phone. |
| childrenrequired | ReactNode | none | The main column. Rendered inside main as a flex column, so children can use flex-1 and min-h-0. |
| className | string | none | Merged onto the root. Use it to fix a height such as h-160 for previews. By default the shell fills its container. |
"Show code under the preview gives the code for the example on the stage."
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- narrow screen
- Below the medium breakpoint the sidebar rail is hidden and only the main area shows.
Tokens
Change one of these and every part that uses it follows.
--bg--fgw-68 rail width
Built in
- The side panel is an area named "Sidebar" and the main area is the page's main area, so screen reader users can jump between them.
- On small screens the side panel is hidden from sight and from screen readers.
- Inside a docs preview the two areas become plain boxes, so the host page keeps one main area.
What you need to do
- Give people another way to reach navigation on small screens, since the side panel is hidden there.
- Use one AppShell per page. Do not put one inside another.
- Put a template such as ThreadTemplate inside it, which adds the page's top heading.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide