Marigold
Marigold

Application

MarigoldProvider
RouterProvider

Layout

Aside
Aspect
Breakout
Center
Columns
Container
Grid
Inline
Inset
Scrollable
Split
Stack
Tiles

Actions

ActionBaralpha
Button
Link
LinkButton
ToggleButtonalpha
ToggleButtonGroupalpha

Form

Autocomplete
Calendar
Checkbox
ComboBox
DateField
DatePicker
FileField
Form
Multiselectdeprecated
NumberField
Radio
SearchField
Select
Slider
Switch
TagFieldbeta
TextArea
TextField
TimeField

Collection

SelectList
Tableupdated
Tag

Navigation

Accordion
Breadcrumbs
Pagination
Sidebarbeta
Tabs
TopNavigationbeta

Overlay

ContextualHelp
Dialog
Drawer
Menu
Toastbeta
Tooltip

Content

Badge
Card
Divider
EmptyStatebeta
Headline
Icon
List
Loader
SectionMessage
SVG
Text

Formatters

DateFormat
NumericFormat

Hooks and Utils

cn
cva
extendTheme
parseFormData
useAsyncListData
useListData
useResponsiveValue
useTheme
VisuallyHidden
Components

Sidebar

Persistent navigation panel for organizing app-level navigation.

The <Sidebar> provides persistent, app-level navigation placed on the left side of the screen. It organizes navigation items into a vertical list, supports drill-down sub-navigation for nested sections, and adapts between a desktop panel and a mobile sheet overlay.

Anatomy

A sidebar consists of a panel alongside the main content area. The panel is organized into three zones: a sticky header, a scrollable navigation area, and a sticky footer.

HeaderGroup labelNavigationSidebarToggleItemSeparatorFooter
  • Header: Sticky top area for branding, logos, or workspace switchers.
  • Group label: A non-interactive section heading to organize items into logical groups.
  • Navigation: Scrollable area containing navigation items with drill-down support.
  • Sidebar: The panel itself, with header, navigation, and footer zones.
  • Toggle: A button placed outside the sidebar to open or close it.
  • Item: A navigation entry that is either a link or a branch that opens a sub-panel.
  • Separator: A visual divider between groups of items.
  • Footer: Sticky bottom area for user profiles, settings, or secondary actions.

Appearance

The appearance of a component can be customized using the variant and size props. These props adjust the visual style and dimensions of the component, available values are based on the active theme.

The selected theme does not has any options for"variant" and "size".
Acme Inc.
DashboardOrdersProducts
Settings
AccountBilling
All productsCategoriesInventory
jane@acme.com
PropertyTypeDescription
variant-The available variants of this component.
size-The available sizes of this component.

Usage

The sidebar is the central navigation of every application. It is part of the default app layout and how users move between sections. It sits on the left side of the screen and is organized into a header for branding, a scrollable navigation area, and a footer for secondary actions. On mobile, it adapts to a sheet overlay.

In most applications, the sidebar is paired with a top navigation to form an L-shaped app shell. The top navigation handles global actions like search, breadcrumbs, and user menus, while the sidebar handles section-level navigation.

The sidebar does not have dedicated icon support yet. If your application needs icons alongside navigation labels, coordinate across all applications first to ensure consistency. For now, use text-only labels.

Header and Footer

Above and below the scrollable navigation sit two sticky areas: the header and the footer. They stay visible while users scroll through nav items, but neither is meant for navigation itself.

Put your branding in the header. The app logo, the product name, or both. It tells users where they are and that is all it should do. When using a top navigation, place the application logo in the sidebar header, not in the top navigation bar.

Secondary actions go in the footer. A link to help or support, a settings shortcut, or a way to send feedback. Things users reach for occasionally, not on every visit.

Organizing navigation

Think about what your users need to get to most often and put those items at the top. People scan vertical lists from top to bottom, so the order matters.

A common mistake is to sort items alphabetically or mirror the internal system structure. Both ignore how people actually work. An "Analytics" link sorted to the top alphabetically is unhelpful if users open it once a month, while "Orders", the thing they check ten times a day, is buried further down. Order by frequency of use, not by the alphabet or your org chart.

When the list gets long, break it into groups using section headings (<Sidebar.GroupLabel>) and visual dividers (<Sidebar.Separator>). Group items by what the user is trying to do, not by how your system is structured internally. Avoid putting user-generated or unbounded content in the navigation, since lists that can grow without limit make the sidebar hard to use.

If users need to see all items at a glance, groups with headings are the right choice. If a section has its own sub-pages that users only visit occasionally, nest them behind a branch item instead. See drill-down navigation for more on nesting.

Do

Keep labels short. Lead with the most meaningful word, since users scan the beginning first. "Order history" beats "History of orders."

Don't

Don't use long, technical labels like "Transaction management module" when a simple word like "Orders" works.

Drill-down navigation

Use drill-down when a group of sub-pages belongs together under one parent, like "Settings" with sub-pages for profile, notifications, and security. Clicking a branch item opens a sub-panel with a back button to return.

Don't use drill-down just to shorten a long list. Every branch adds a click, so it should only be used when the sub-pages form a clear group.

The sidebar supports one level of nesting. More levels would force users to remember where they are in a deep hierarchy, and navigating back through multiple levels gets tedious fast. If your content needs more depth, reconsider how you organize your sections rather than adding more nesting.

My App
DashboardOrdersCustomers
Settings
ProfileNotificationsSecurity

Dashboard

Click "Settings" to see the drill-down panel.
import { useState } from 'react';import { Headline, Sidebar, Text } from '@marigold/components';import { RouterProvider } from '@marigold/components';export default () => {  const [currentPath, setCurrentPath] = useState('/dashboard');  return (    <RouterProvider navigate={setCurrentPath}>      <Sidebar.Provider>        <div className="flex h-100">          <Sidebar>            <Sidebar.Header>              <Text weight="bold">My App</Text>            </Sidebar.Header>            <Sidebar.Nav>              <Sidebar.Item                href="/dashboard"                active={currentPath === '/dashboard'}              >                Dashboard              </Sidebar.Item>              <Sidebar.Item href="/orders" active={currentPath === '/orders'}>                Orders              </Sidebar.Item>              <Sidebar.Item                href="/customers"                active={currentPath === '/customers'}              >                Customers              </Sidebar.Item>              <Sidebar.Separator />              <Sidebar.Item id="settings" textValue="Settings">                Settings                <Sidebar.Item                  href="/settings/profile"                  active={currentPath === '/settings/profile'}                >                  Profile                </Sidebar.Item>                <Sidebar.Item                  href="/settings/notifications"                  active={currentPath === '/settings/notifications'}                >                  Notifications                </Sidebar.Item>                <Sidebar.Item                  href="/settings/security"                  active={currentPath === '/settings/security'}                >                  Security                </Sidebar.Item>              </Sidebar.Item>            </Sidebar.Nav>          </Sidebar>          <main className="flex-1 p-4">            <Sidebar.Toggle />            <Headline level={2}>              {currentPath                .replace(/^\/settings\//, '')                .replace('/', '')                .replace(/-/g, ' ')                .replace(/^\w/, c => c.toUpperCase())}            </Headline>            <Text>Click "Settings" to see the drill-down panel.</Text>          </main>        </div>      </Sidebar.Provider>    </RouterProvider>  );};

Detail pages

The sidebar shows sections and categories, not individual records. When a user opens a specific order from the "Orders" page, the detail view fills the main content area. The sidebar does not change. "Orders" stays highlighted so the user can tell which section they are in.

Do not add individual records as sidebar items. A sidebar that lists every order or ticket grows without limit and becomes unusable. The sidebar points to the list; the list points to the detail.

For hierarchies deeper than two levels, pair the sidebar with breadcrumbs. The sidebar lets users move between sections, breadcrumbs show the path back (e.g. Orders > ORD-4712).

My App
DashboardOrdersCustomers
Orders
...
  1. Orders

Orders

Order
Customer
Total
ORD-4712
Anna Schmidt
€129.00
ORD-4713
Max Weber
€84.50
ORD-4714
Lena Fischer
€212.00
import { useState } from 'react';import {  Breadcrumbs,  Headline,  Inline,  Link,  NumericFormat,  RouterProvider,  Sidebar,  Stack,  Table,  Text,} from '@marigold/components';const orders = [  { id: 'ORD-4712', customer: 'Anna Schmidt', total: 129 },  { id: 'ORD-4713', customer: 'Max Weber', total: 84.5 },  { id: 'ORD-4714', customer: 'Lena Fischer', total: 212 },];const pages: Record<string, string> = {  '/dashboard': 'Dashboard',  '/orders': 'Orders',  '/customers': 'Customers',};const OrderList = () => (  <Stack space={4}>    <Headline level={2}>Orders</Headline>    <Table aria-label="Orders" selectionMode="none">      <Table.Header>        <Table.Column>Order</Table.Column>        <Table.Column>Customer</Table.Column>        <Table.Column>Total</Table.Column>      </Table.Header>      <Table.Body>        {orders.map(order => (          <Table.Row key={order.id}>            <Table.Cell>              <Link href={`/orders/${order.id}`}>{order.id}</Link>            </Table.Cell>            <Table.Cell>{order.customer}</Table.Cell>            <Table.Cell>              <NumericFormat                value={order.total}                style="currency"                currency="EUR"              />            </Table.Cell>          </Table.Row>        ))}      </Table.Body>    </Table>  </Stack>);const OrderDetail = ({ id }: { id: string }) => {  const order = orders.find(o => o.id === id);  if (!order) return null;  return (    <Stack space={4}>      <Headline level={2}>{order.id}</Headline>      <Text>        Customer: {order.customer}        <br />        Total:{' '}        <NumericFormat value={order.total} style="currency" currency="EUR" />      </Text>    </Stack>  );};export default () => {  const [currentPath, setCurrentPath] = useState('/orders');  const orderMatch = currentPath.match(/^\/orders\/(.+)$/);  const selectedOrder = orderMatch?.[1] ?? null;  const activePath = selectedOrder ? '/orders' : currentPath;  return (    <RouterProvider navigate={setCurrentPath}>      <Sidebar.Provider>        <div className="flex h-100">          <Sidebar>            <Sidebar.Header>              <Text weight="bold">My App</Text>            </Sidebar.Header>            <Sidebar.Nav>              <Sidebar.Item                href="/dashboard"                active={activePath === '/dashboard'}              >                Dashboard              </Sidebar.Item>              <Sidebar.Item href="/orders" active={activePath === '/orders'}>                Orders              </Sidebar.Item>              <Sidebar.Item                href="/customers"                active={activePath === '/customers'}              >                Customers              </Sidebar.Item>            </Sidebar.Nav>          </Sidebar>          <main className="grid flex-1 grid-rows-[auto_1fr] gap-8 overflow-auto pl-4">            <Inline alignY="center">              <Sidebar.Toggle />              <Breadcrumbs>                <Breadcrumbs.Item href={activePath}>                  {pages[activePath] ?? activePath}                </Breadcrumbs.Item>                {selectedOrder && (                  <Breadcrumbs.Item href={`/orders/${selectedOrder}`}>                    {selectedOrder}                  </Breadcrumbs.Item>                )}              </Breadcrumbs>            </Inline>            {activePath === '/orders' && !selectedOrder && <OrderList />}            {activePath === '/orders' && selectedOrder && (              <OrderDetail id={selectedOrder} />            )}            {activePath === '/dashboard' && (              <Headline level={2}>Dashboard</Headline>            )}            {activePath === '/customers' && (              <Headline level={2}>Customers</Headline>            )}          </main>        </div>      </Sidebar.Provider>    </RouterProvider>  );};
Don't

Don't add individual records as sidebar items. The sidebar points to the list; the list points to the detail.

Do

For hierarchies deeper than two levels, pair the sidebar with breadcrumbs to show the path back.

Collapsing

Users can collapse or expand the sidebar with the toggle button or the keyboard shortcut Cmd+B / Ctrl+B. The sidebar remembers this preference across page loads. When combined with a top navigation, the sidebar toggle button is typically placed in the top navigation's start slot.

Accessibility

The sidebar renders as a <nav> element so screen readers can identify it as a navigation landmark. The keyboard shortcut Cmd+B / Ctrl+B toggles it globally. When drilling into a sub-panel, focus moves to the back button, and returns to the branch trigger when navigating back. The active item is announced as the current page. On mobile, the overlay traps focus and can be dismissed with Escape. Built-in strings support German and English localization.

Props

Did you know? You can explore, test, and customize props live in Marigold's storybook. Watch the effects they have in real-time!
View Sidebar stories

Sidebar.Provider

Prop

Type

Sidebar

Prop

Type

Sidebar.Header

Prop

Type

Sidebar.Nav

Prop

Type

Sidebar.Item

Prop

Type

Sidebar.GroupLabel

Prop

Type

Sidebar.Footer

Prop

Type

Alternative components

  • Tabs: Use when content groups are at the same hierarchy level and users switch between views within a single page, not across the application.
  • Accordion: Use for collapsing content sections within a page, not for app-level navigation.
  • Breadcrumbs: Use to show the user's location within a hierarchy. Breadcrumbs complement a sidebar but don't replace it.
  • Top Navigation: Use for the horizontal top bar that houses global actions like search, breadcrumbs, and user menus. Pair it with a sidebar for the standard L-shaped app shell.
Last update: 3 minutes ago

Pagination

Component that divides up large datasets into manageable chunks.

Tabs

Component for building tabs.

On this page

AnatomyAppearanceUsageHeader and FooterOrganizing navigationDrill-down navigationDetail pagesCollapsingAccessibilityPropsSidebar.ProviderSidebarSidebar.HeaderSidebar.NavSidebar.ItemSidebar.GroupLabelSidebar.FooterAlternative components