Home
'use client'
import { Spacer } from '@chakra-ui/react'
import { SaasUILogo } from '@saas-ui/assets'
import { AppShell } from '#components/ui/app-shell'
import { IconButton } from '#components/ui/icon-button'
import { Menu } from '#components/ui/menu'
import { Navbar } from '#components/ui/navbar'
import { Page } from '#components/ui/page'
import { Persona } from '#components/ui/persona'
import { Sidebar } from '#components/ui/sidebar'
import { FiHome, FiSettings, FiUsers } from 'react-icons/fi'
export const AppShellBasic = () => {
return (
<Sidebar.Provider>
<AppShell
height="480px"
header={
<Navbar.Root borderBottomWidth="1px" borderColor="border.subtle">
<Navbar.Content>
<Navbar.Brand>
<SaasUILogo width="80px" />
</Navbar.Brand>
<Spacer />
<Menu.Root>
<Menu.Trigger asChild>
<IconButton variant="ghost" size="sm" aria-label="User menu">
<Persona.Root presence="online">
<Persona.Avatar size="xs" src="/showcase-avatar.jpg" />
</Persona.Root>
</IconButton>
</Menu.Trigger>
<Menu.Content>
<Menu.Item value="signout">Sign out</Menu.Item>
</Menu.Content>
</Menu.Root>
</Navbar.Content>
</Navbar.Root>
}
sidebar={
<Sidebar.Root width="240px">
<Sidebar.Body>
<Sidebar.Group>
<Sidebar.GroupContent>
<Sidebar.NavItem>
<Sidebar.NavButton active>
<FiHome /> Home
</Sidebar.NavButton>
</Sidebar.NavItem>
<Sidebar.NavItem>
<Sidebar.NavButton>
<FiUsers /> Contacts
</Sidebar.NavButton>
</Sidebar.NavItem>
<Sidebar.NavItem>
<Sidebar.NavButton>
<FiSettings /> Settings
</Sidebar.NavButton>
</Sidebar.NavItem>
</Sidebar.GroupContent>
</Sidebar.Group>
</Sidebar.Body>
</Sidebar.Root>
}
>
<Page.Root>
<Page.Header title="Home" />
<Page.Body textStyle="sm">Your application content</Page.Body>
</Page.Root>
</AppShell>
</Sidebar.Provider>
)
}
Anatomy
import { AppShell } from '#components/ui/app-shell'<AppShell
header={<Navbar.Root />}
sidebar={<Sidebar.Root />}
aside={<Aside.Root />}
footer={<Footer />}
>
{/* Main content */}
</AppShell>Usage
The AppShell component provides a structured layout container that helps you build consistent application interfaces. It manages the composition and positioning of key UI elements like navbars and sidebars.
The header and footer slots span the full width of the shell. The
sidebar (left) and aside (right) slots are rendered next to the main
content area, which contains the children and scrolls independently.
AppShell fits itself into the viewport by default, using height="100dvh".
Set the height prop to constrain it, or use the fullscreen prop to fix it
to the viewport instead.
Navbar layout
Use the header slot to render a Navbar above the
main content.
'use client'
import { SaasUILogo } from '@saas-ui/assets'
import { AppShell } from '#components/ui/app-shell'
import { Navbar } from '#components/ui/navbar'
import { Page } from '#components/ui/page'
import { SearchInput } from '#components/ui/search-input'
export const AppShellNavbar = () => {
return (
<AppShell
height="400px"
header={
<Navbar.Root borderBottomWidth="1px" borderColor="border.subtle">
<Navbar.Content>
<Navbar.Brand>
<SaasUILogo width="80px" />
</Navbar.Brand>
<Navbar.ItemGroup>
<Navbar.Item>
<Navbar.Link active aria-current="page" href="#">
Home
</Navbar.Link>
</Navbar.Item>
<Navbar.Item>
<Navbar.Link href="#">About</Navbar.Link>
</Navbar.Item>
<Navbar.Item>
<Navbar.Link href="#">Pricing</Navbar.Link>
</Navbar.Item>
</Navbar.ItemGroup>
<Navbar.ItemGroup justifyContent="flex-end">
<Navbar.Item>
<SearchInput size="sm" />
</Navbar.Item>
</Navbar.ItemGroup>
</Navbar.Content>
</Navbar.Root>
}
>
<Page.Root>
<Page.Body textStyle="sm">Your application content</Page.Body>
</Page.Root>
</AppShell>
)
}
Sidebar layout
Use the sidebar slot to render a Sidebar next to
the main content. The sidebar requires a Sidebar.Provider around the shell
to manage its state.
Contacts
'use client'
import { Spacer } from '@chakra-ui/react'
import { SaasUILogo } from '@saas-ui/assets'
import { AppShell } from '#components/ui/app-shell'
import { IconButton } from '#components/ui/icon-button'
import { Menu } from '#components/ui/menu'
import { Page } from '#components/ui/page'
import { Persona } from '#components/ui/persona'
import { Sidebar } from '#components/ui/sidebar'
import { FiHome, FiUsers } from 'react-icons/fi'
export const AppShellSidebar = () => {
return (
<Sidebar.Provider>
<AppShell
height="400px"
sidebar={
<Sidebar.Root width="240px">
<Sidebar.Header ps="4">
<SaasUILogo width="80px" />
<Spacer />
<Menu.Root>
<Menu.Trigger asChild>
<IconButton variant="ghost" size="sm" aria-label="User menu">
<Persona.Root presence="online">
<Persona.Avatar size="xs" src="/showcase-avatar.jpg" />
</Persona.Root>
</IconButton>
</Menu.Trigger>
<Menu.Content>
<Menu.Item value="signout">Sign out</Menu.Item>
</Menu.Content>
</Menu.Root>
</Sidebar.Header>
<Sidebar.Body>
<Sidebar.Group>
<Sidebar.GroupContent>
<Sidebar.NavItem>
<Sidebar.NavButton>
<FiHome /> Home
</Sidebar.NavButton>
</Sidebar.NavItem>
<Sidebar.NavItem>
<Sidebar.NavButton active>
<FiUsers /> Contacts
</Sidebar.NavButton>
</Sidebar.NavItem>
</Sidebar.GroupContent>
</Sidebar.Group>
</Sidebar.Body>
</Sidebar.Root>
}
>
<Page.Root>
<Page.Header title="Contacts" />
<Page.Body textStyle="sm">Your application content</Page.Body>
</Page.Root>
</AppShell>
</Sidebar.Provider>
)
}
Aside
Use the aside slot to render a secondary Aside
panel on the right side of the main content, for example to show details of a
selected item.
Contacts
Details
Contact details
'use client'
import { Text } from '@chakra-ui/react'
import { AppShell } from '#components/ui/app-shell'
import { Aside } from '#components/ui/aside'
import { Page } from '#components/ui/page'
export const AppShellAside = () => {
return (
<AppShell
height="400px"
aside={
<Aside.Root
defaultOpen
width="240px"
borderLeftWidth="1px"
bg="bg.panel"
>
<Aside.Header>
<Aside.Title>Details</Aside.Title>
</Aside.Header>
<Aside.Body>
<Text textStyle="sm">Contact details</Text>
</Aside.Body>
</Aside.Root>
}
>
<Page.Root>
<Page.Header title="Contacts" />
<Page.Body textStyle="sm">Your application content</Page.Body>
</Page.Root>
</AppShell>
)
}
Fullscreen
Use the fullscreen prop to fix the shell to the viewport. This prevents
scroll bouncing of the entire page, giving your app a native feel.
<AppShell fullscreen>{/* Main content */}</AppShell>Props
AppShell
| Prop | Default | Type |
|---|---|---|
header | React.ReactNodeThe top header navigation | |
sidebar | React.ReactElementMain sidebar, positioned on the left | |
aside | React.ReactNodeSecondary sidebar, positioned on the right | |
footer | React.ReactNodeThe footer |