Layout
AppShell
Application layout with a module rail, a navigation panel, a mobile header and a bottom navigation, independent of any router.
<script>
import { AppShellLayout, type NavModule } from "@codeatlasdev/ui";
import { goto } from "$app/navigation"; // or any router
import { page } from "$app/state";
const modules: NavModule[] = [/* id, label, icon, basePath, items */];
</script>
<AppShellLayout {modules} pathname={page.url.pathname} onNavigate={(path) => goto(path)}>
{#snippet header()}<SiteHeader />{/snippet}
{@render children()}
</AppShellLayout>
The shell never imports a router. You pass the current pathname and handle onNavigate, so it works with SvelteKit, Astro, or plain state. onNavigate fires when the user switches module from the rail or the bottom navigation. Panel items are plain <a href> links, so a client-side router takes them over like any other link. Put it only on application routes, never on marketing pages. Below 900px the rail and panel disappear and the mobile header plus bottom navigation take over. See the templates for complete applications built on it.
Anatomy
Rail (52px), panel (200px) and the main content area.
52px
200px
Code
Live shell
The real component inside a fixed-height frame. The pathname lives in local state; click the rail and panel to navigate.
Revenue
Users
Click a rail icon or a panel item. The demo keeps the pathname in local state.
Code
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modules | NavModule[] | – | Navigation modules. Each has id, label, icon, basePath, optional pathPrefixes and items (NavItem or NavSection). |
pathname | string | – | Current location. Decides the active module and item. |
onNavigate | (path: string) => void | – | Called with the destination path when the rail, panel or bottom navigation is used. |
defaultModule | number | 0 | Module index used before any navigation matches. |
persistPanel | boolean | true | Persists the open or closed state of the panel in localStorage. |
header | Snippet | – | Desktop header rendered above the page content. Hidden below 900px. |
brand | Snippet | – | Logo shown at the top of the rail and in the mobile header. |
railBottom | Snippet | – | Items pinned to the bottom of the rail, such as an avatar or notifications. |
mobileHeaderActions | Snippet | – | Controls at the trailing edge of the mobile header. Use it for anything that must stay reachable below 900px. |
mobileTitle | Snippet | – | Replaces the plain page title in the mobile header, for example a tappable title that opens a sheet. |
mainId | string | null | 'main' | `id` of the content region, the skip link target. Pass `null` when your page renders its own `<main id="main" tabindex="-1">`. |
skipLink | boolean | true | Renders the SkipLink. Turn it off when the shell is embedded in a page that already has one. |
children | Snippet | – | Page content. |