Layout
App layout composition patterns and components
Layout
The mdxui layout system provides a complete set of components for building authenticated SaaS applications. This guide covers layout composition patterns, from the root App wrapper through Shell, Sidebar, Header, and content areas.
Layout Architecture
The layout system follows a hierarchical composition pattern:
App (Authentication & Providers)
└── Shell (Layout Structure)
├── Sidebar (Navigation)
├── Header (Top Bar)
└── Main (Content Area)
└── View (Dashboard, Settings, etc.)Core Layout Components
App Wrapper
The App component is the root container providing authentication, theme, and global context.
interface AppProps {
/** App display name */
name: string
/** Authentication configuration */
auth?: {
provider: 'workos' | 'clerk' | 'auth0' | 'custom'
clientId?: string
loginUrl?: string
redirectUri?: string
}
/** Child content */
children: React.ReactNode
}Usage:
<App name="Acme Dashboard" auth={{ provider: 'workos' }}>
<Shell>
<Dashboard />
</Shell>
</App>See App component documentation for details.
Shell Component
The Shell provides the structural layout with sidebar and header positioning.
interface ShellProps {
/** Shell children (Views) */
children: React.ReactNode
/** Sidebar configuration */
sidebar?: {
visible: boolean
collapsed: boolean
collapsedWidth: number
width: number
position: 'left' | 'right'
showToggle: boolean
persistState: boolean
}
/** Header configuration */
header?: {
visible: boolean
height: number
sticky: boolean
bordered: boolean
}
/** Theme mode */
theme?: 'light' | 'dark' | 'system'
/** Loading state */
loading?: boolean
/** Shell variant */
variant?: 'default' | 'compact' | 'minimal'
}Usage:
<Shell>
<Dashboard />
</Shell>See Shell component documentation for details.
Layout Modes
The Shell supports different layout configurations for various use cases.
Standard Mode (Default)
Full sidebar with header:
┌─────────────────────────────────────┐
│ Header │
├──────────┬──────────────────────────┤
│ │ │
│ Sidebar │ Main │
│ │ │
│ │ │
└──────────┴──────────────────────────┘<Shell>
<Dashboard />
</Shell>Sidebar-Only Mode
Hide header for vertical focus:
┌──────────┬──────────────────────────┐
│ │ │
│ Sidebar │ Main │
│ │ │
│ │ │
└──────────┴──────────────────────────┘<Shell
header={{
visible: false
}}
>
<Dashboard />
</Shell>Header-Only Mode
Hide sidebar for horizontal focus:
┌─────────────────────────────────────┐
│ Header │
├─────────────────────────────────────┤
│ │
│ Main │
│ │
│ │
└─────────────────────────────────────┘<Shell
sidebar={{
visible: false
}}
>
<Dashboard />
</Shell>Full-Screen Mode
Hide both for immersive experiences:
┌─────────────────────────────────────┐
│ │
│ Main │
│ │
│ │
└─────────────────────────────────────┘<Shell
sidebar={{ visible: false }}
header={{ visible: false }}
>
<EditorView />
</Shell>Compact Mode
Collapsed sidebar by default:
┌─────────────────────────────────────┐
│ Header │
├───┬─────────────────────────────────┤
│ S │ │
│ i │ Main │
│ d │ │
│ e │ │
└───┴─────────────────────────────────┘<Shell
sidebar={{
collapsed: true
}}
>
<Dashboard />
</Shell>Sidebar Component
The Sidebar provides primary navigation with collapsible sections and user menu.
interface SidebarProps {
/** Navigation items */
items: ShellNavItem[]
/** Footer items */
footerItems?: ShellNavItem[]
/** Is collapsed */
collapsed?: boolean
/** Collapse handler */
onCollapse?: () => void
/** Logo/branding */
logo?: {
src: string
collapsedSrc?: string
alt: string
href?: string
}
/** User info */
user?: UserContext
/** Show workspace switcher */
showWorkspaceSwitcher?: boolean
/** Show search */
showSearch?: boolean
}See Sidebar component documentation for details.
Sidebar Navigation Patterns
Flat Navigation
Simple list of top-level items:
const navigation = [
{ id: 'dashboard', label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{ id: 'projects', label: 'Projects', href: '/projects', icon: 'Folder' },
{ id: 'team', label: 'Team', href: '/team', icon: 'Users' },
{ id: 'settings', label: 'Settings', href: '/settings', icon: 'Settings' },
]
<Sidebar items={navigation} />Grouped Navigation
Items organized into collapsible sections:
const navigation = [
{ id: 'dashboard', label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{
id: 'developer',
label: 'Developer',
href: '/developer',
icon: 'Code',
children: [
{ id: 'api-keys', label: 'API Keys', href: '/keys', icon: 'Key' },
{ id: 'webhooks', label: 'Webhooks', href: '/webhooks', icon: 'Webhook' },
{ id: 'logs', label: 'Logs', href: '/logs', icon: 'FileText' },
]
},
{ id: 'settings', label: 'Settings', href: '/settings', icon: 'Settings' },
]
<Sidebar items={navigation} />Multi-Level Navigation
Deep hierarchy with sections:
const navigation = [
{
id: 'workspace',
label: 'Workspace',
type: 'heading'
},
{ id: 'dashboard', label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{ id: 'projects', label: 'Projects', href: '/projects', icon: 'Folder' },
{
id: 'separator-1',
type: 'separator'
},
{
id: 'developer',
label: 'Developer',
type: 'heading'
},
{ id: 'api-keys', label: 'API Keys', href: '/keys', icon: 'Key' },
{ id: 'webhooks', label: 'Webhooks', href: '/webhooks', icon: 'Webhook' },
]
<Sidebar items={navigation} />Header Component
The Header provides the top bar with breadcrumbs, search, notifications, and user menu.
interface HeaderProps {
/** Title/page name */
title?: string
/** Subtitle/description */
subtitle?: string
/** Navigation items */
nav?: ShellNavItem[]
/** Show breadcrumbs */
showBreadcrumbs?: boolean
/** Breadcrumb items */
breadcrumbs?: Array<{
label: string
href?: string
}>
/** Show search */
showSearch?: boolean
/** Show notifications */
showNotifications?: boolean
/** Notification count */
notificationCount?: number
/** User context */
user?: UserContext
/** Show user menu */
showUserMenu?: boolean
/** Is sticky */
sticky?: boolean
/** Show border */
bordered?: boolean
}See Header component documentation for details.
Header Patterns
Standard Header
Full header with all features:
<Header
breadcrumbs={[
{ label: 'Dashboard', href: '/' },
{ label: 'Projects', href: '/projects' },
{ label: 'Acme Website' },
]}
showSearch={true}
showNotifications={true}
notificationCount={3}
user={currentUser}
/>Minimal Header
Focus on current location:
<Header
title="Dashboard"
subtitle="Welcome back"
user={currentUser}
showSearch={false}
showNotifications={false}
/>Context Header
Page-specific actions:
<Header
breadcrumbs={breadcrumbs}
user={currentUser}
right={
<>
<Button variant="outline">Export</Button>
<Button>Create Project</Button>
</>
}
/>Breadcrumbs
The Breadcrumbs component shows hierarchical location context.
interface BreadcrumbsProps {
/** Breadcrumb items */
items: Array<{
label: string
href?: string
icon?: string
current?: boolean
}>
/** Separator character */
separator?: string
/** Show home icon */
showHome?: boolean
/** Home href */
homeHref?: string
/** Max items before collapse */
maxItems?: number
}Usage:
<Breadcrumbs
items={[
{ label: 'Dashboard', href: '/' },
{ label: 'Projects', href: '/projects' },
{ label: 'Acme Website', current: true },
]}
/>Renders: Dashboard > Projects > Acme Website
Dynamic Breadcrumbs
Generate breadcrumbs from the current route:
function useBreadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/').filter(Boolean)
const breadcrumbs = [
{ label: 'Dashboard', href: '/' }
]
let path = ''
segments.forEach((segment, index) => {
path += `/${segment}`
const isLast = index === segments.length - 1
breadcrumbs.push({
label: formatSegment(segment),
href: isLast ? undefined : path,
current: isLast,
})
})
return breadcrumbs
}
<Header breadcrumbs={useBreadcrumbs()} />Layout Composition Patterns
Developer App Layout
API-focused application:
<App name="Developer Portal" auth={{ provider: 'workos' }}>
<Shell>
<Sidebar
items={[
{ label: 'Overview', href: '/', icon: 'LayoutDashboard' },
{ label: 'API Keys', href: '/keys', icon: 'Key' },
{ label: 'Webhooks', href: '/webhooks', icon: 'Webhook' },
{ label: 'Usage', href: '/usage', icon: 'BarChart' },
{ label: 'Logs', href: '/logs', icon: 'FileText' },
{ label: 'Docs', href: '/docs', icon: 'Book' },
{ label: 'Settings', href: '/settings', icon: 'Settings' },
]}
user={currentUser}
/>
<Header
breadcrumbs={breadcrumbs}
showSearch={true}
showNotifications={true}
user={currentUser}
/>
<Dashboard />
</Shell>
</App>Admin App Layout
CRUD-focused interface:
<App name="Admin Panel" auth={{ provider: 'clerk' }}>
<Shell>
<Sidebar
items={[
{ label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{
label: 'Collections',
icon: 'Database',
children: [
{ label: 'Posts', href: '/posts' },
{ label: 'Pages', href: '/pages' },
{ label: 'Media', href: '/media' },
]
},
{
label: 'Access',
icon: 'Shield',
children: [
{ label: 'Users', href: '/users' },
{ label: 'Roles', href: '/roles' },
]
},
{ label: 'Settings', href: '/settings', icon: 'Settings' },
]}
user={currentUser}
/>
<Header breadcrumbs={breadcrumbs} user={currentUser} />
<CollectionView collection="posts" />
</Shell>
</App>SaaS App Layout
Multi-tenant product:
<App name="Acme SaaS" auth={{ provider: 'auth0' }}>
<Shell>
<Sidebar
items={[
{ label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{ label: 'Projects', href: '/projects', icon: 'Folder' },
{ label: 'Team', href: '/team', icon: 'Users' },
{ label: 'Billing', href: '/billing', icon: 'CreditCard' },
{ label: 'Settings', href: '/settings', icon: 'Settings' },
]}
user={currentUser}
showWorkspaceSwitcher={true}
/>
<Header
breadcrumbs={breadcrumbs}
showSearch={true}
showNotifications={true}
user={currentUser}
/>
<Dashboard />
</Shell>
</App>CRM App Layout
Customer relationship management:
<App name="CRM" auth={{ provider: 'workos' }}>
<Shell>
<Sidebar
items={[
{ label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{ label: 'Contacts', href: '/contacts', icon: 'Users' },
{ label: 'Deals', href: '/deals', icon: 'TrendingUp' },
{ label: 'Activities', href: '/activities', icon: 'Calendar' },
{ label: 'Reports', href: '/reports', icon: 'BarChart' },
{ label: 'Settings', href: '/settings', icon: 'Settings' },
]}
user={currentUser}
/>
<Header breadcrumbs={breadcrumbs} user={currentUser} />
<ContactsView />
</Shell>
</App>VibeCode Layout
Three-panel development environment:
<App name="VibeCode" auth={{ provider: 'clerk' }}>
<Shell
sidebar={{ visible: false }}
header={{ visible: true }}
>
<Header
title="VibeCode"
showSearch={false}
user={currentUser}
/>
<VibeCodeView>
<ChatPanel />
<PreviewPanel />
<CodePanel />
</VibeCodeView>
</Shell>
</App>Responsive Layout
The layout system adapts automatically to different screen sizes.
Desktop (≥1024px)
Full layout with expanded sidebar:
┌────────────────────────────────────┐
│ Header │
├─────────┬──────────────────────────┤
│ Sidebar │ Main │
│ (240px) │ │
│ │ │
└─────────┴──────────────────────────┘Tablet (768px - 1023px)
Collapsed sidebar:
┌────────────────────────────────────┐
│ Header │
├───┬────────────────────────────────┤
│ S │ Main │
│ b │ │
│ │ │
└───┴────────────────────────────────┘Mobile (under 768px)
Sidebar as overlay drawer:
┌────────────────────────────────────┐
│ ☰ Header │
├────────────────────────────────────┤
│ │
│ Main │
│ │
└────────────────────────────────────┘Customization
Custom Sidebar Width
<Shell
sidebar={{
width: 280,
collapsedWidth: 80,
}}
>
<Dashboard />
</Shell>Custom Header Height
<Shell
header={{
height: 72,
}}
>
<Dashboard />
</Shell>Custom Breakpoints
Control responsive behavior with CSS variables:
<Shell
className="
[--sidebar-width:280px]
[--sidebar-collapsed-width:80px]
[--header-height:72px]
"
>
<Dashboard />
</Shell>Theme Variants
<Shell variant="minimal">
<Dashboard />
</Shell>
<Shell variant="compact">
<Dashboard />
</Shell>State Management
Sidebar State
The Shell manages sidebar collapse state:
function MyApp() {
const [collapsed, setCollapsed] = useState(false)
return (
<Shell
sidebar={{
collapsed,
onCollapse: () => setCollapsed(!collapsed),
persistState: true, // Save to localStorage
}}
>
<Dashboard />
</Shell>
)
}Navigation State
Track active routes and breadcrumbs:
function useNavigation() {
const pathname = usePathname()
const breadcrumbs = generateBreadcrumbs(pathname)
const activeNavItem = findActiveNavItem(pathname)
return { breadcrumbs, activeNavItem }
}
function MyApp() {
const { breadcrumbs, activeNavItem } = useNavigation()
return (
<Shell>
<Header breadcrumbs={breadcrumbs} />
<Dashboard />
</Shell>
)
}Accessibility
The layout system implements accessibility best practices:
- Landmark regions - Proper semantic HTML (
<header>,<nav>,<main>) - Skip links - Jump to main content
- Keyboard navigation - Full keyboard support
- Focus management - Visible focus indicators
- Screen reader labels - Descriptive ARIA labels
- Responsive - Mobile-friendly navigation
Example markup:
<div role="application">
<a href="#main" class="skip-link">Skip to content</a>
<header role="banner">
<!-- Header content -->
</header>
<nav role="navigation" aria-label="Main navigation">
<!-- Sidebar navigation -->
</nav>
<main id="main" role="main">
<!-- Main content -->
</main>
</div>Best Practices
Keep Layout Declarative
Define layout structure upfront:
// Good - clear layout structure
<App name="Dashboard">
<Shell>
<Dashboard />
</Shell>
</App>
// Bad - conditional layout
<App name="Dashboard">
{showSidebar ? (
<Shell>
<Dashboard />
</Shell>
) : (
<Dashboard />
)}
</App>Use Consistent Navigation
Maintain the same navigation structure across the app:
// Good - consistent nav structure
const navigation = [
{ label: 'Dashboard', href: '/', icon: 'LayoutDashboard' },
{ label: 'Settings', href: '/settings', icon: 'Settings' },
]
// Use the same navigation everywhere
<Sidebar items={navigation} />Handle Mobile Navigation
Ensure mobile users can access all features:
<Shell
sidebar={{
// Automatically handle mobile drawer
persistState: true,
}}
>
<Dashboard />
</Shell>Limit Breadcrumb Depth
Keep breadcrumbs shallow (3-4 levels max):
// Good - shallow hierarchy
{ label: 'Dashboard', href: '/' }
{ label: 'Projects', href: '/projects' }
{ label: 'Acme Website' }
// Bad - too deep
{ label: 'Dashboard', href: '/' }
{ label: 'Workspace', href: '/workspace' }
{ label: 'Category', href: '/workspace/category' }
{ label: 'Subcategory', href: '/category/sub' }
{ label: 'Projects', href: '/sub/projects' }
{ label: 'Acme Website' }Use View Components
Wrap content in semantic View components:
// Good - semantic views
<Shell>
<DashboardView>
<MetricsPanel />
<ChartPanel />
</DashboardView>
</Shell>
// Bad - raw content
<Shell>
<div>
<h1>Dashboard</h1>
<p>Metrics...</p>
</div>
</Shell>