← All @molecule/* packages · App templates
@molecule/app-sidebar-layout-reactFeature · sidebar-layout · App (browser) · v1.0.1 · Apache-2.0
Vertical sidebar app shell: brand block + vertical NavLinks + user-menu slot, with React Router Outlet for nested routes
npm install @molecule/app-sidebar-layout-react@molecule/app-sidebar-layout-react is a ready-made sidebar-layout feature for the app (browser) side. It composes the core interfaces it needs, so it works with whichever providers your app has bonded.
import { SidebarLayout } from '@molecule/app-sidebar-layout-react'
import type { SidebarNavItem } from '@molecule/app-sidebar-layout-react'
const NAV: SidebarNavItem[] = [
{ key: 'dashboard', to: '/dashboard', icon: 'dashboard', label: 'Dashboard' },
{ key: 'projects', to: '/projects', icon: 'folder', label: 'Projects' },
{ key: 'settings', to: '/settings', icon: 'settings', label: 'Settings' },
]
function AppShell() {
return <SidebarLayout appName="Acme App" navItems={NAV} />
}Works with: @molecule/app-react, @molecule/app-ui
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Vertical sidebar app shell layout.
Exports <SidebarLayout> plus the SidebarLayoutProps and
SidebarNavItem types — fixed-width left sidebar with brand link,
vertical nav links, and bottom user-menu / theme-toggle slots; the main
area on the right scrolls and renders React Router's <Outlet /> for
nested routes.
import { SidebarLayout } from '@molecule/app-sidebar-layout-react'
import type { SidebarNavItem } from '@molecule/app-sidebar-layout-react'
const NAV: SidebarNavItem[] = [
{ key: 'dashboard', to: '/dashboard', icon: 'dashboard', label: 'Dashboard' },
{ key: 'projects', to: '/projects', icon: 'folder', label: 'Projects' },
{ key: 'settings', to: '/settings', icon: 'settings', label: 'Settings' },
]
function AppShell() {
return <SidebarLayout appName="Acme App" navItems={NAV} />
}
Mount it as a LAYOUT ROUTE so child routes render in the main area:
a parent route with element={<AppShell />} and your pages as child
routes.
feature
npm install @molecule/app-sidebar-layout-react @molecule/app-react @molecule/app-ui react react-router
npm install -D @types/react
SidebarLayoutPropsProps accepted by the {@link SidebarLayout} component.
interface SidebarLayoutProps {
/** Brand text shown at the top of the sidebar (typically the app name). */
appName: string
/** Path the brand link navigates to. Defaults to `'/'`. */
logoTo?: string
/** Vertical nav items rendered in the sidebar. */
navItems: ReadonlyArray<SidebarNavItem>
/** Slot rendered at the bottom of the sidebar (typically a `<UserMenu />`). */
userMenu?: ReactNode
/** Optional slot rendered next to the user menu (typically a `<ThemeToggle />`). */
themeToggle?: ReactNode
/** Aria-label for the primary <nav>. */
navAriaLabel?: string
/**
* Sidebar width — a stack-agnostic preset (`'sm'` | `'md'` | `'lg'`) or an
* exact pixel `number`. Applied via inline style so no Tailwind width utility
* couples consumers to the styling library. Defaults to `'md'` (240px).
*/
sidebarWidth?: SidebarWidth
/**
* Legacy sidebar-width prop retained only for backward compatibility.
*
* @deprecated Use {@link SidebarLayoutProps.sidebarWidth} instead. A raw
* Tailwind width utility (`'w-60'`, `'w-64'`) coupled consumers to Tailwind.
* Still accepted for backward compatibility: a `w-<n>` value is PARSED to a
* pixel width and applied via inline style (never re-emitted as a class), so
* old callers keep working without reintroducing the coupling. `sidebarWidth`
* takes precedence when both are supplied.
*/
sidebarWidthClass?: string
/** Extra classes on the outer wrapper. */
className?: string
/** `data-mol-id` for AI-agent selectors. */
dataMolId?: string
}
SidebarNavItemDescribes a single item in the sidebar's vertical navigation list.
interface SidebarNavItem {
/** Route path the link goes to. */
to: string
/** Stable key — used for React keys and i18n key suffix. */
key: string
/** Material symbol icon name. */
icon?: string
/** Visible label. Apps that route this through `t(...)` should pass the resolved string. */
label: string
}
SidebarWidthSemantic sidebar width. Mapped to a fixed pixel width internally and applied via inline style (a width is one of the "specific values" a styling-agnostic ClassMap cannot express as a swappable token) so the sidebar renders the same regardless of which ClassMap bond is active — no Tailwind width utility leaks into consumer code.
sm → 208px, md → 240px (default), lg → 256px.number for an exact pixel width.type SidebarWidth = 'sm' | 'md' | 'lg' | number
SidebarLayout(props)Vertical sidebar shell with brand + vertical nav + bottom user-menu slot.
function SidebarLayout({
appName,
logoTo = '/',
navItems,
userMenu,
themeToggle,
navAriaLabel = 'Primary navigation',
sidebarWidth,
sidebarWidthClass,
className,
dataMolId,
}: SidebarLayoutProps): ReactElement<unknown, string | JSXElementConstructor<any>>
props — Component props.Peer dependencies:
@molecule/app-react ^1.0.1@molecule/app-ui ^1.0.1react ^18.0.0 || ^19.0.0react-router ^7.0.0 || ^8.0.0@molecule/app-react
@molecule/app-ui
react
react-router
Router required: calls useLocation() and renders <Outlet /> — it
throws outside a react-router <Router>, and the main area stays empty
unless it is a layout route with child routes.
Fully ClassMap-driven: every layout/surface/border/text/state class is
resolved through getClassMap() (cm.pageShell, cm.page, cm.surface,
cm.borderR, cm.bgPrimarySubtle/cm.textPrimary for the active item,
cm.textMuted/cm.link for the rest, …), so swapping the ClassMap bond
restyles the whole shell — no raw Tailwind/Material-3 utility class is
baked in. The sidebar WIDTH is the one "specific value" a styling-agnostic
ClassMap can't express, so it is applied via inline style: use the
stack-agnostic sidebarWidth prop ('sm'|'md'|'lg'|pixel number),
NOT a Tailwind width utility. The old sidebarWidthClass prop is
@deprecated — still accepted, but its w-<n> value is parsed to pixels
(never re-emitted as a class).
icon values are Material Symbols ligature names rendered with the
material-symbols-outlined font class — the one documented icon-font
exception (a font ligature the consumer supplies as data, not a hardcoded
chrome glyph). Without the Material Symbols font loaded, the raw icon NAME
shows as text — omit icon when the font is not shipped.
Active-nav highlighting picks the longest prefix-match of the current
path; same-path clicks get a #top fragment appended (same behavior as
@molecule/app-safe-link-react).
navAriaLabel defaults to English "Primary navigation" — pass a
translated string in localized apps (nav labels are plain strings;
translate them upstream).
Requires a bonded ClassMap. Sibling: @molecule/app-shell-layout-react
is the top header/footer shell (router-free).