---
title: "AppShell"
description: "The back office frame: a 232 px sidebar, a sticky topbar and a flexible main. The skip link comes first and the content changes routes without remounting the frame."
kind: block
id: C-053
category: erp
registry: "app-shell"
docs: "https://koten.codehive.com.co/blocks/erp/app-shell"
markdown: "https://koten.codehive.com.co/md/blocks/erp/app-shell.md"
install: "npx shadcn@latest add https://koten.codehive.com.co/r/app-shell.json"
language: en
---

# AppShell · シェル

> Block C-053 in the Koten UI catalog.

The back office frame: a 232 px sidebar, a sticky topbar and a flexible main. The skip link comes first and the content changes routes without remounting the frame.

> Japanese labels in this page are documentation metadata. Keep the consumer UI in the product's language and do not add Japanese decoration unless the user asks (R-031).

## Install

Install `koten-theme` first when the project does not have Koten tokens yet. Use the runner of your project.

```bash
# Node
npx shadcn@latest add https://koten.codehive.com.co/r/app-shell.json

# Bun
bunx shadcn@latest add https://koten.codehive.com.co/r/app-shell.json
```

## Files

- `@/components/patterns/app-shell.tsx`
- `@/lib/koten/cn.ts`
- `@/lib/koten/format.ts`
- `@/lib/koten/intl.ts`
- `@/lib/koten/labels.ts`
- `@/lib/koten/provider.tsx`

## Requirements

- **client** — Client rendering: The shell keeps the active navigation item in state. In an SSR framework, mount the shell inside a client boundary so the interaction survives.

## Composition

This block composes Koten primitives. Read the source files above before changing its anatomy.

## Examples

### Full shell

Sidebar, topbar and main in one grid. Navigation changes the active state without a reload.

```tsx
<AppShell
  skipLabel="Skip to content"
  brand={<Brand />}
  sidebarLabel="Main navigation"
  sidebar={<SideNav label="Main" groups={groups} onNavigate={navigate} />}
  topbar={
    <TopBar
      left={<Breadcrumb items={crumbs} label="Breadcrumb" />}
      search={<Input type="search" aria-label="Search" placeholder="Order, customer or SKU" />}
      actions={<SyncState />}
    />
  }
>
  <PageHeader
    eyebrow="Sales"
    title="Orders"
    description="Search, filter and process orders. Filters live in the URL."
    actions={<Button size="sm" variant="primary">New order</Button>}
  />
  <OrdersTable />
</AppShell>
```

## Props

| Prop | Type | Default | Notes |
| --- | --- | --- | --- |
| `skipLabel` | `string` | `—` | Label of the link that jumps to main; it is the page's first focus stop. |
| `skipHref` | `string` | `"#contenido"` | Skip link target; it must match the main id or an existing anchor. |
| `brand` | `ReactNode` | `—` | Brand in the sidebar header; the row is 48 px tall. |
| `sidebar` | `ReactNode` | `—` | Contents of the aside; usually a SideNav. |
| `sidebarLabel` | `string` | `—` | aria-label of the complementary landmark wrapping the sidebar. |
| `topbar` | `ReactNode` | `—` | Header above main; usually the route's TopBar. |
| `children` | `ReactNode` | `—` | Main content; page padding is already applied. |
| `className` | `string` | `—` | Adds to the component's base classes. |

## Accessibility

- The skip link is the first focusable element and jumps to the main with id contenido.
- The aside is a complementary landmark named by sidebarLabel; the inner navigation carries its own name.
- Below 1024 px the sidebar is not shown: the mobile menu belongs in the TopBar, not duplicated while hidden.
- The skip link is visible only on focus; do not hide it with outline: none.

## Sources

- Catalog page: https://koten.codehive.com.co/blocks/erp/app-shell
- Markdown: https://koten.codehive.com.co/md/blocks/erp/app-shell.md
- Repository: https://github.com/codehive-colombia/koten
