Skip to content
zedkit
@zedtech/zedkit-ui

AppShell

Sidebar + topbar shell with a mobile drawer, optional fixed-height scrollports, and page header (title, description, actions).

Stable@zedtech/zedkit-ui

Wraps: vaul

Page state

Harbor Studio

Tasks

Website relaunch · everything the team has to ship before the launch on November 25.

Open tasks

1 blocked

10
Waiting for review

Reviewers were notified

2
Completed

Since the project started

2 of 12

Write the homepage copy

Priya Nair

In review
Nov 3

Redirect the old product URLs

Luca Moretti

In progress
Nov 4

Compress and resize hero images

Tom Becker

Done
Oct 28

Choose the new typeface pairing

Aiko Tanaka

Done
Oct 26

Connect the contact form to email

Luca Moretti

Blocked
Nov 5

Review the checkout flow on phones

Maya Chen

In progress
Nov 6

Translate the pricing page to French

Priya Nair

To do
Nov 10

Set up the analytics dashboard

Tom Becker

To do
Nov 12

Test keyboard navigation on every page

Aiko Tanaka

In review
Nov 7

Archive the 2024 blog posts

Maya Chen

To do
Nov 18
Page 1 of 2
View props table
Who is it for?
Teams scaffolding a consistent admin layout.
When should I use it?
As the frame of every admin page.
When should I pick something else?
For public marketing pages — use your site layout instead.

Use it

Examples import from the package. With copied source, import from @/components/ui/app-shell instead.

Simple
import { AppShell, PageHeader } from "@zedtech/zedkit-ui";
<AppShell sidebar={<OrgNav />} topbar={<UserMenu />}>
  <PageHeader title="Items" description="All items." />
</AppShell>

Without topbar

Projects

Four active projects, two due this week.

import { AppShell } from "@zedtech/zedkit-ui";
<AppShell sidebar={<nav aria-label="Primary"><a href="#">Overview</a></nav>}> {/* topbar is optional */}
  <p>Content.</p>
</AppShell>

Fixed height

Alpha

Open the menu on a narrow viewport to switch runs.

import { AppShell } from "@zedtech/zedkit-ui";
<AppShell sidebar={<nav aria-label="Runs">Runs</nav>} fixedHeight>
  <p>Live transcript.</p>
</AppShell>

Custom trigger

Alpha

Open the menu on a narrow viewport to switch runs.

import { AppShell } from "@zedtech/zedkit-ui";
<AppShell sidebar={<nav aria-label="Runs">Runs</nav>} sidebarTrigger={<button type="button">Runs</button>}>
  <p>Content.</p>
</AppShell>
// Opt out of the mobile menu: sidebarTrigger={null}

Kit not installed yet?

You get
Every component from one import, with the theme tokens included. Updates arrive with your package manager.
You need
  • React 19 and Tailwind CSS 4, for example a Next.js app created with the Tailwind option.
  1. Install

    Terminal
    pnpm add @zedtech/zedkit-ui @fontsource-variable/inter @fontsource-variable/space-grotesk
  2. Load the styles

    Tailwind does not scan node_modules, so the @source line points it at the kit's class names. The path is relative to this file.

    src/app/globals.cssReplace the contents
    @import "tailwindcss";
    @import "@zedtech/zedkit-ui/styles.css";
    @source "../../node_modules/@zedtech/zedkit-ui/src/**/*.{ts,tsx}";
    @source not "../../node_modules/@zedtech/zedkit-ui/src/**/*.test.*";
    
  3. Pick a look

    Set data-identity="glass" on <html> so the finished app matches the site. Fresh app: replace src/app/layout.tsx. Existing app: merge, and keep ThemeProvider and other providers.

    src/app/layout.tsxReplace the contents
    import "./globals.css";
    import "./fonts";
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en" data-identity="glass">
          <body>{children}</body>
        </html>
      );
    }
    
  4. Load the fonts

    The Glass look uses Inter and Space Grotesk; these imports self-host them.

    src/app/fonts.tsCreate the file
    import "@fontsource-variable/inter";
    import "@fontsource-variable/space-grotesk";
    
  5. Import and render

    src/app/page.tsxReplace the contents
    import { AppShell } from "@zedtech/zedkit-ui";
    
    export default function Page() {
      return (
        <main className="px-page-gutter py-section-sm">
          <AppShell sidebar={<nav aria-label="Primary"><a href="#">Overview</a></nav>}> {/* topbar is optional */}
            <p>Content.</p>
          </AppShell>
        </main>
      );
    }
    

Package version 2.0.1.

Props

Prop

Type

Accessibility

Landmark structure (aside/header/main) comes free; below md the menu opens a left Drawer titled from messages.mainNavigation (focus trap and return-focus come from Drawer). A custom sidebarTrigger must be a focusable button or link with an accessible name. main (always) and the aside (when fixedHeight) take tabIndex={0} so the overflow scrollports are keyboard reachable. Sidebar width and main padding come from --spacing-app-shell-* theme tokens.

Troubleshooting

Below `md`, the built-in menu (or `sidebarTrigger`) opens the sidebar in a Drawer; pass `sidebarTrigger={null}` to hide it on small screens. A custom `sidebarTrigger` must be a focusable button or link with an accessible name. The sidebar node mounts twice (aside + drawer) — keep it presentational: no ids, no aria references, no effects (duplicate-id risk). `fixedHeight` makes sidebar and main independent scrollports. `mainRef` is the scrolling `<main>` and needs a client parent (AppShell stays a server module). Cramped content — main padding is `p-app-shell-padding` / `md:p-app-shell-padding-md`.

On this page