From d95ff5bda979f910698edf0741a9ccbe92fbbbfb Mon Sep 17 00:00:00 2001
From: Danny White <3104761+dnywh@users.noreply.github.com>
Date: Wed, 7 Oct 2026 10:36:41 +1100
Subject: [PATCH] docs(design-system): rewrite sidebar page for monorepo tokens
(#51165)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Problem
The design-system Sidebar page was mostly an upstream shadcn paste:
first-person voice, a broken `/blocks` link, missing structure images,
CLI install steps that do not match this monorepo, and a long changelog
/ data-fetching tutorial that do not apply here.
Separately, it still taught classic shadcn **HSL channel** variables
plus `hsl(var(--sidebar-*))`. In this monorepo those tokens are **full
colours**. Mixing the two patterns produces invalid CSS.
Related call-site cleanup:
https://github.com/supabase/supabase/pull/51161
## Solution
- Rewrite the Sidebar docs as a shorter monorepo guide: import from
`'ui'`, structure, theming, provider / sidebar props, menu building
blocks, controlled mode, state styling.
- Move Studio’s `--sidebar-*` aliases into shared `packages/ui` compat
CSS so every app on the shared theme gets working `bg-sidebar`
utilities.
- Drop the duplicate definitions from Studio `globals.css`.
Left alone on purpose: brand / destructive channel tokens and docs that
correctly use `hsl(var(--brand-…))`.
## Review instructions
Design-system preview:
[design-system](https://design-system-git-dnywh-docssidebar-full-colour-tokens-supabase.vercel.app/)
1. [Live Sidebar
docs](https://supabase.com/design-system/docs/components/sidebar) ·
[Preview Sidebar
docs](https://design-system-git-dnywh-docssidebar-full-colour-tokens-supabase.vercel.app/design-system/docs/components/sidebar).
Confirm the page is no longer the upstream essay: no broken images, no
`/blocks` link, imports from `'ui'`, theming shows full-colour aliases.
2. Smoke Studio: left nav should look unchanged (same aliases, now from
compat.css).
3. Optional: in DevTools, confirm `--sidebar-background` resolves to a
full `oklch(...)` colour.
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
---
.../components/component-preview.tsx | 4 +-
.../design-system/components/source-panel.tsx | 22 +-
.../content/docs/components/sidebar.mdx | 1183 ++---------------
.../registry/default/example/sidebar-demo.tsx | 69 +
apps/design-system/registry/examples.ts | 6 +
apps/studio/styles/globals.css | 21 +-
packages/ui/build/css/source/compat.css | 12 +
.../ui/src/components/shadcn/ui/sidebar.tsx | 2 +-
8 files changed, 220 insertions(+), 1099 deletions(-)
create mode 100644 apps/design-system/registry/default/example/sidebar-demo.tsx
diff --git a/apps/design-system/components/component-preview.tsx b/apps/design-system/components/component-preview.tsx
index 1381d131a38..b9898033daa 100644
--- a/apps/design-system/components/component-preview.tsx
+++ b/apps/design-system/components/component-preview.tsx
@@ -97,7 +97,7 @@ export function ComponentPreview({
)
}
diff --git a/apps/design-system/content/docs/components/sidebar.mdx b/apps/design-system/content/docs/components/sidebar.mdx
index db88a7747b3..9fe3ef86115 100644
--- a/apps/design-system/content/docs/components/sidebar.mdx
+++ b/apps/design-system/content/docs/components/sidebar.mdx
@@ -1,173 +1,32 @@
---
title: Sidebar
-description: A composable, themeable and customizable sidebar component.
+description: A composable, themeable sidebar for app navigation.
component: true
+source:
+ shadcn: true
---
-Sidebars are one of the most complex components to build. They are central
-to any application and often contain a lot of moving parts.
+
-I don't like building sidebars. So I built 30+ of them. All kinds of
-configurations. Then I extracted the core components into `sidebar.tsx`.
-
-We now have a solid foundation to build on top of. Composable. Themeable.
-Customizable.
-
-[Browse the Blocks Library](/blocks).
-
-## Installation
-
-
-
-
- CLI
- Manual
-
-
-
-
-
-Run the following command to install `sidebar.tsx`
-
-```bash
-npx shadcn@latest add sidebar
-```
-
-Add the following colors to your CSS file
-
-The command above should install the colors for you. If not, copy and paste the following in your CSS file.
-
-We'll go over the colors later in the [theming section](/docs/components/sidebar#theming).
-
-```css title="app/globals.css"
-@layer base {
- :root {
- --sidebar-background: 0 0% 98%;
- --sidebar-foreground: 240 5.3% 26.1%;
- --sidebar-primary: 240 5.9% 10%;
- --sidebar-primary-foreground: 0 0% 98%;
- --sidebar-accent: 240 4.8% 95.9%;
- --sidebar-accent-foreground: 240 5.9% 10%;
- --sidebar-border: 220 13% 91%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-
- .dark {
- --sidebar-background: 240 5.9% 10%;
- --sidebar-foreground: 240 4.8% 95.9%;
- --sidebar-primary: 224.3 76.3% 48%;
- --sidebar-primary-foreground: 0 0% 100%;
- --sidebar-accent: 240 3.7% 15.9%;
- --sidebar-accent-foreground: 240 4.8% 95.9%;
- --sidebar-border: 240 3.7% 15.9%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-}
-```
-
-
-
-
-
-
-
-
-
-Copy and paste the following code into your project.
-
-
-
-Update the import paths to match your project setup.
-
-Add the following colors to your CSS file
-
-We'll go over the colors later in the [theming section](/docs/components/sidebar#theming).
-
-```css title="app/globals.css"
-@layer base {
- :root {
- --sidebar-background: 0 0% 98%;
- --sidebar-foreground: 240 5.3% 26.1%;
- --sidebar-primary: 240 5.9% 10%;
- --sidebar-primary-foreground: 0 0% 98%;
- --sidebar-accent: 240 4.8% 95.9%;
- --sidebar-accent-foreground: 240 5.9% 10%;
- --sidebar-border: 220 13% 91%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-
- .dark {
- --sidebar-background: 240 5.9% 10%;
- --sidebar-foreground: 240 4.8% 95.9%;
- --sidebar-primary: 224.3 76.3% 48%;
- --sidebar-primary-foreground: 0 0% 100%;
- --sidebar-accent: 240 3.7% 15.9%;
- --sidebar-accent-foreground: 240 4.8% 95.9%;
- --sidebar-border: 240 3.7% 15.9%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-}
-```
-
-Add the Sidebar tailwind config into `tailwind.config.js`
-
-Add the following object in the `theme.extend.colors` section of your `tailwind.config.js` file
-this config enable sidebar related style utilities like bg-sidebar
-
-```javascript title="tailwind.config.js"
-// ...
-sidebar: {
- DEFAULT: 'hsl(var(--sidebar-background))',
- foreground: 'hsl(var(--sidebar-foreground))',
- primary: 'hsl(var(--sidebar-primary))',
- 'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
- accent: 'hsl(var(--sidebar-accent))',
- 'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
- border: 'hsl(var(--sidebar-border))',
- ring: 'hsl(var(--sidebar-ring))',
-},
-// ...
-```
-
-
-
-
-
-
-
-## Structure
-
-A `Sidebar` component is composed of the following parts:
-
-- `SidebarProvider` - Handles collapsible state.
-- `Sidebar` - The sidebar container.
-- `SidebarHeader` and `SidebarFooter` - Sticky at the top and bottom of the sidebar.
-- `SidebarContent` - Scrollable content.
-- `SidebarGroup` - Section within the `SidebarContent`.
-- `SidebarTrigger` - Trigger for the `Sidebar`.
-
-
-
+Composable sidebar primitives for app chrome. The implementation lives in
+`packages/ui` and is used by Studio. Import from `'ui'`.
## Usage
-```tsx showLineNumbers title="app/layout.tsx"
-import { AppSidebar } from '@/components/app-sidebar'
-import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
+```tsx
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarGroup,
+ SidebarHeader,
+ SidebarProvider,
+ SidebarTrigger,
+} from 'ui'
+```
-export default function Layout({ children }: { children: React.ReactNode }) {
+```tsx
+export function Layout({ children }: { children: React.ReactNode }) {
return (
@@ -180,22 +39,13 @@ export default function Layout({ children }: { children: React.ReactNode }) {
}
```
-```tsx showLineNumbers title="components/app-sidebar.tsx"
-import {
- Sidebar,
- SidebarContent,
- SidebarFooter,
- SidebarGroup,
- SidebarHeader,
-} from '@/components/ui/sidebar'
-
+```tsx
export function AppSidebar() {
return (
-
@@ -203,269 +53,101 @@ export function AppSidebar() {
}
```
-## Your First Sidebar
+## Structure
-Let's start with the most basic sidebar. A collapsible sidebar with a menu.
+| Part | Role |
+| --------------------------------- | ---------------------------------------------------- |
+| `SidebarProvider` | Open / collapsed state and keyboard shortcut |
+| `Sidebar` | Collapsible shell (`side`, `variant`, `collapsible`) |
+| `SidebarHeader` / `SidebarFooter` | Sticky top and bottom regions |
+| `SidebarContent` | Scrollable middle |
+| `SidebarGroup` | Section inside content |
+| `SidebarMenu` and friends | Nav items, actions, badges, submenus |
+| `SidebarTrigger` / `SidebarRail` | Toggle controls |
-
+## Theming
-Add a `SidebarProvider` and `SidebarTrigger` at the root of your application.
+Sidebar colours use **full colour** CSS variables (aliases into the semantic
+system), not classic shadcn HSL channel lists. Do not wrap them in `hsl()`.
-```tsx showLineNumbers title="app/layout.tsx"
-import { AppSidebar } from '@/components/app-sidebar'
-import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
+Shared defaults (also in `packages/ui` compat CSS, matching Studio):
-export default function Layout({ children }: { children: React.ReactNode }) {
- return (
-
-
-
-
- {children}
-
-
- )
+```css
+:root {
+ --sidebar-background: var(--background-dash-sidebar);
+ --sidebar-foreground: var(--foreground-default);
+ --sidebar-primary: var(--foreground-default);
+ --sidebar-primary-foreground: var(--warning);
+ --sidebar-accent: var(--background-selection);
+ --sidebar-accent-foreground: var(--foreground-default);
+ --sidebar-border: var(--border-default);
+ --sidebar-ring: var(--ring);
}
```
-Create a new sidebar component at `components/app-sidebar.tsx`.
+Apps that import `config/tailwind.config.css` already map these to
+`--color-sidebar*` in `packages/config/css/theme.css`, so utilities like
+`bg-sidebar` and `text-sidebar-foreground` work without a local Tailwind colour
+entry.
-```tsx showLineNumbers title="components/app-sidebar.tsx"
-import { Sidebar, SidebarContent } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return (
-
-
-
- )
-}
-```
-
-Now, let's add a `SidebarMenu` to the sidebar.
-
-We'll use the `SidebarMenu` component in a `SidebarGroup`.
-
-```tsx showLineNumbers title="components/app-sidebar.tsx"
-import { Calendar, Home, Inbox, Search, Settings } from 'lucide-react'
-
-import {
- Sidebar,
- SidebarContent,
- SidebarGroup,
- SidebarGroupContent,
- SidebarGroupLabel,
- SidebarMenu,
- SidebarMenuButton,
- SidebarMenuItem,
-} from '@/components/ui/sidebar'
-
-// Menu items.
-const items = [
- {
- title: 'Home',
- url: '#',
- icon: Home,
- },
- {
- title: 'Inbox',
- url: '#',
- icon: Inbox,
- },
- {
- title: 'Calendar',
- url: '#',
- icon: Calendar,
- },
- {
- title: 'Search',
- url: '#',
- icon: Search,
- },
- {
- title: 'Settings',
- url: '#',
- icon: Settings,
- },
-]
-
-export function AppSidebar() {
- return (
-
-
-
- Application
-
-
- {items.map((item) => (
-
-
-
-
- {item.title}
-
-
-
- ))}
-
-
-
-
-
- )
-}
-```
-
-You've created your first sidebar.
-
-
-
-## Components
-
-The components in `sidebar.tsx` are built to be composable i.e you build your sidebar by putting the provided components together. They also compose well with other shadcn/ui components such as `DropdownMenu`, `Collapsible` or `Dialog` etc.
-
-**If you need to change the code in `sidebar.tsx`, you are encouraged to do so. The code is yours. Use `sidebar.tsx` as a starting point and build your own.**
-
-In the next sections, we'll go over each component and how to use them.
+Override any `--sidebar-*` token when the sidebar should diverge from the main
+app surface.
## SidebarProvider
-The `SidebarProvider` component is used to provide the sidebar context to the `Sidebar` component. You should always wrap your application in a `SidebarProvider` component.
+Always wrap the sidebar and its trigger in `SidebarProvider`.
-### Props
-
-| Name | Type | Description |
-| -------------- | ------------------------- | -------------------------------------------- |
-| `defaultOpen` | `boolean` | Default open state of the sidebar. |
-| `open` | `boolean` | Open state of the sidebar (controlled). |
-| `onOpenChange` | `(open: boolean) => void` | Sets open state of the sidebar (controlled). |
+| Prop | Type | Description |
+| -------------- | ------------------------- | ------------------------------- |
+| `defaultOpen` | `boolean` | Uncontrolled initial open state |
+| `open` | `boolean` | Controlled open state |
+| `onOpenChange` | `(open: boolean) => void` | Controlled open setter |
### Width
-If you have a single sidebar in your application, you can use the `SIDEBAR_WIDTH` and `SIDEBAR_WIDTH_MOBILE` variables in `sidebar.tsx` to set the width of the sidebar.
+Override desktop width with `--sidebar-width` on the provider (also used for
+layout spacing):
-```tsx showLineNumbers title="components/ui/sidebar.tsx"
-const SIDEBAR_WIDTH = '16rem'
-const SIDEBAR_WIDTH_MOBILE = '18rem'
-```
-
-For multiple sidebars in your application, you can use the `style` prop to set the width of the sidebar.
-
-To set the width of the sidebar, you can use the `--sidebar-width` and `--sidebar-width-mobile` CSS variables in the `style` prop.
-
-```tsx showLineNumbers title="components/ui/sidebar.tsx"
+```tsx
```
-This will handle the width of the sidebar but also the layout spacing.
+Defaults live as `SIDEBAR_WIDTH` / `SIDEBAR_WIDTH_ICON` in
+`packages/ui/src/components/shadcn/ui/sidebar.tsx`. On mobile, the sheet sets
+`--sidebar-width` from the hard-coded `SIDEBAR_WIDTH_MOBILE` constant, so there
+is no separate CSS variable to override mobile width today.
-### Keyboard Shortcut
+### Persisted state
-The `SIDEBAR_KEYBOARD_SHORTCUT` variable is used to set the keyboard shortcut used to open and close the sidebar.
-
-To trigger the sidebar, you use the `cmd+b` keyboard shortcut on Mac and `ctrl+b` on Windows.
-
-You can change the keyboard shortcut by updating the `SIDEBAR_KEYBOARD_SHORTCUT` variable.
-
-```tsx showLineNumbers title="components/ui/sidebar.tsx"
-const SIDEBAR_KEYBOARD_SHORTCUT = 'b'
-```
-
-### Persisted State
-
-The `SidebarProvider` supports persisting the sidebar state across page reloads and server-side rendering. It uses cookies to store the current state of the sidebar. When the sidebar state changes, a default cookie named `sidebar_state` is set with the current open/closed state. This cookie is then read on subsequent page loads to restore the sidebar state.
-
-To persist sidebar state in Next.js, set up your `SidebarProvider` in `app/layout.tsx` like this:
-
-```tsx showLineNumbers title="app/layout.tsx"
-import { cookies } from 'next/headers'
-
-import { AppSidebar } from '@/components/app-sidebar'
-import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'
-
-export async function Layout({ children }: { children: React.ReactNode }) {
- const cookieStore = await cookies()
- const defaultOpen = cookieStore.get('sidebar_state')?.value === 'true'
-
- return (
-
-
-
-
- {children}
-
-
- )
-}
-```
-
-You can change the name of the cookie by updating the `SIDEBAR_COOKIE_NAME` variable in `sidebar.tsx`.
-
-```tsx showLineNumbers title="components/ui/sidebar.tsx"
-const SIDEBAR_COOKIE_NAME = 'sidebar_state'
-```
+`SidebarProvider` writes open state to a cookie (`SIDEBAR_COOKIE_NAME`, default
+`sidebar:state` in this repo). For Next.js App Router you can seed
+`defaultOpen` from that cookie on the server.
## Sidebar
-The main `Sidebar` component used to render a collapsible sidebar.
+| Prop | Type | Description |
+| ------------- | ------------------------------------ | ------------------ |
+| `side` | `'left' \| 'right'` | Which edge |
+| `variant` | `'sidebar' \| 'floating' \| 'inset'` | Visual treatment |
+| `collapsible` | `'offcanvas' \| 'icon' \| 'none'` | Collapse behaviour |
-```tsx showLineNumbers
-import { Sidebar } from '@/components/ui/sidebar'
+| `collapsible` | Behaviour |
+| ------------- | ------------------ |
+| `offcanvas` | Slides off-canvas |
+| `icon` | Collapses to icons |
+| `none` | Always expanded |
-export function AppSidebar() {
- return
-}
-```
+For `variant="inset"`, wrap main content in `SidebarInset`.
-### Props
-
-| Property | Type | Description |
-| ------------- | --------------------------------- | --------------------------------- |
-| `side` | `left` or `right` | The side of the sidebar. |
-| `variant` | `sidebar`, `floating`, or `inset` | The variant of the sidebar. |
-| `collapsible` | `offcanvas`, `icon`, or `none` | Collapsible state of the sidebar. |
-
-### side
-
-Use the `side` prop to change the side of the sidebar.
-
-Available options are `left` and `right`.
-
-```tsx showLineNumbers
-import { Sidebar } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return
-}
-```
-
-### variant
-
-Use the `variant` prop to change the variant of the sidebar.
-
-Available options are `sidebar`, `floating` and `inset`.
-
-```tsx showLineNumbers
-import { Sidebar } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return
-}
-```
-
-
- **Note:** If you use the `inset` variant, remember to wrap your main content in a `SidebarInset`
- component.
-
-
-```tsx showLineNumbers
+```tsx
@@ -474,625 +156,52 @@ export function AppSidebar() {
```
-### collapsible
-
-Use the `collapsible` prop to make the sidebar collapsible.
-
-Available options are `offcanvas`, `icon` and `none`.
-
-```tsx showLineNumbers
-import { Sidebar } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return
-}
-```
-
-| Prop | Description |
-| ----------- | ------------------------------------------------------------ |
-| `offcanvas` | A collapsible sidebar that slides in from the left or right. |
-| `icon` | A sidebar that collapses to icons. |
-| `none` | A non-collapsible sidebar. |
-
## useSidebar
-The `useSidebar` hook is used to control the sidebar.
-
-```tsx showLineNumbers
-import { useSidebar } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- const { state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar } = useSidebar()
-}
-```
-
-| Property | Type | Description |
-| --------------- | ------------------------- | --------------------------------------------- |
-| `state` | `expanded` or `collapsed` | The current state of the sidebar. |
-| `open` | `boolean` | Whether the sidebar is open. |
-| `setOpen` | `(open: boolean) => void` | Sets the open state of the sidebar. |
-| `openMobile` | `boolean` | Whether the sidebar is open on mobile. |
-| `setOpenMobile` | `(open: boolean) => void` | Sets the open state of the sidebar on mobile. |
-| `isMobile` | `boolean` | Whether the sidebar is on mobile. |
-| `toggleSidebar` | `() => void` | Toggles the sidebar. Desktop and mobile. |
-
-## SidebarHeader
-
-Use the `SidebarHeader` component to add a sticky header to the sidebar.
-
-The following example adds a `` to the `SidebarHeader`.
-
-```tsx showLineNumbers title="components/app-sidebar.tsx"
-
-
-
-
-
-
-
- Select Workspace
-
-
-
-
-
- Acme Inc
-
-
- Acme Corp.
-
-
-
-
-
-
-
-```
-
-## SidebarFooter
-
-Use the `SidebarFooter` component to add a sticky footer to the sidebar.
-
-The following example adds a `` to the `SidebarFooter`.
-
-```tsx showLineNumbers title="components/app-sidebar.tsx"
-export function AppSidebar() {
- return (
-
-
-
-
-
-
-
-
-
-
- Username
-
-
-
-
-
- Account
-
-
- Billing
-
-
- Sign out
-
-
-
-
-
-
-
-
- )
-}
-```
-
-## SidebarContent
-
-The `SidebarContent` component is used to wrap the content of the sidebar. This is where you add your `SidebarGroup` components. It is scrollable.
-
-```tsx showLineNumbers
-import { Sidebar, SidebarContent } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return (
-
-
-
-
-
-
- )
-}
-```
-
-## SidebarGroup
-
-Use the `SidebarGroup` component to create a section within the sidebar.
-
-A `SidebarGroup` has a `SidebarGroupLabel`, a `SidebarGroupContent` and an optional `SidebarGroupAction`.
-
-```tsx showLineNumbers
-import { Sidebar, SidebarContent, SidebarGroup } from '@/components/ui/sidebar'
-
-export function AppSidebar() {
- return (
-
-
-
- Application
-
- Add Project
-
-
-
-
-
- )
-}
-```
-
-## Collapsible SidebarGroup
-
-To make a `SidebarGroup` collapsible, wrap it in a `Collapsible`.
-
-```tsx showLineNumbers
-export function AppSidebar() {
- return (
-
-
-
-
- Help
-
-
-
-
-
-
-
-
- )
-}
-```
-
-
- **Note:** We wrap the `CollapsibleTrigger` in a `SidebarGroupLabel` to render a button.
-
-
-## SidebarGroupAction
-
-Use the `SidebarGroupAction` component to add an action button to the `SidebarGroup`.
-
-```tsx showLineNumbers {5-7}
-export function AppSidebar() {
- return (
-
- Projects
-
- Add Project
-
-
-
- )
-}
-```
-
-## SidebarMenu
-
-The `SidebarMenu` component is used for building a menu within a `SidebarGroup`.
-
-A `SidebarMenu` component is composed of `SidebarMenuItem`, `SidebarMenuButton`, `` and `` components.
-
-
-
-
-Here's an example of a `SidebarMenu` component rendering a list of projects.
-
-```tsx showLineNumbers
-
-
-
- Projects
-
-
- {projects.map((project) => (
-
-
-
-
- {project.name}
-
-
-
- ))}
-
-
-
-
-
-```
-
-## SidebarMenuButton
-
-The `SidebarMenuButton` component is used to render a menu button within a `SidebarMenuItem`.
-
-### Link or Anchor
-
-By default, the `SidebarMenuButton` renders a button but you can use the `asChild` prop to render a different component such as a `Link` or an `a` tag.
-
-```tsx showLineNumbers
-
- Home
-
-```
-
-### Icon and Label
-
-You can render an icon and a truncated label inside the button. Remember to wrap the label in a ``.
-
-```tsx showLineNumbers
-
-
-
- Home
-
-
-```
-
-### isActive
-
-Use the `isActive` prop to mark a menu item as active.
-
-```tsx showLineNumbers
-
- Home
-
-```
-
-## SidebarMenuAction
-
-The `SidebarMenuAction` component is used to render a menu action within a `SidebarMenuItem`.
-
-This button works independently of the `SidebarMenuButton` i.e you can have the `` as a clickable link and the `` as a button.
-
-```tsx showLineNumbers
-
-
-
-
- Home
-
-
-
- Add Project
-
-
-```
-
-### DropdownMenu
-
-Here's an example of a `SidebarMenuAction` component rendering a `DropdownMenu`.
-
-```tsx showLineNumbers
-
-
-
-
- Home
-
-
-
-
-
-
-
-
-
-
- Edit Project
-
-
- Delete Project
-
-
-
-
-```
-
-## SidebarMenuSub
-
-The `SidebarMenuSub` component is used to render a submenu within a `SidebarMenu`.
-
-Use `` and `` to render a submenu item.
-
-```tsx showLineNumbers
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## Collapsible SidebarMenu
-
-To make a `SidebarMenu` component collapsible, wrap it and the `SidebarMenuSub` components in a `Collapsible`.
-
-```tsx showLineNumbers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-## SidebarMenuBadge
-
-The `SidebarMenuBadge` component is used to render a badge within a `SidebarMenuItem`.
-
-```tsx showLineNumbers
-
-
- 24
-
-```
-
-## SidebarMenuSkeleton
-
-The `SidebarMenuSkeleton` component is used to render a skeleton for a `SidebarMenu`. You can use this to show a loading state when using React Server Components, SWR or react-query.
-
-```tsx showLineNumbers
-function NavProjectsSkeleton() {
- return (
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
- ))}
-
- )
-}
-```
-
-## SidebarSeparator
-
-The `SidebarSeparator` component is used to render a separator within a `Sidebar`.
-
-```tsx showLineNumbers
-
-
-
-
-
-
-
-
-
-```
-
-## SidebarTrigger
-
-Use the `SidebarTrigger` component to render a button that toggles the sidebar.
-
-The `SidebarTrigger` component must be used within a `SidebarProvider`.
-
-```tsx showLineNumbers
-
-
-
-
-
-
-```
-
-### Custom Trigger
-
-To create a custom trigger, you can use the `useSidebar` hook.
-
-```tsx showLineNumbers
-import { useSidebar } from '@/components/ui/sidebar'
+```tsx
+import { useSidebar } from 'ui'
export function CustomTrigger() {
const { toggleSidebar } = useSidebar()
-
return
}
```
-## SidebarRail
+Must be used under `SidebarProvider`.
-The `SidebarRail` component is used to render a rail within a `Sidebar`. This rail can be used to toggle the sidebar.
+## Menu building blocks
-```tsx showLineNumbers
-
-
-
-
-
-
-
-
-```
+Typical nav group:
-## Data Fetching
-
-### React Server Components
-
-Here's an example of a `SidebarMenu` component rendering a list of projects using React Server Components.
-
-```tsx showLineNumbers {6} title="Skeleton to show loading state."
-function NavProjectsSkeleton() {
- return (
+```tsx
+
+ Application
+
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
- ))}
-
- )
-}
-```
-
-```tsx showLineNumbers {2} title="Server component fetching data."
-async function NavProjects() {
- const projects = await fetchProjects()
-
- return (
-
- {projects.map((project) => (
-
+ {items.map((item) => (
+
-
-
- {project.name}
+
+
+ {item.title}
))}
- )
-}
+
+
```
-```tsx showLineNumbers {8-10} title="Usage with React Suspense."
-function AppSidebar() {
- return (
-
-
-
- Projects
-
- }>
-
-
-
-
-
-
- )
-}
-```
+Also available: `SidebarMenuAction`, `SidebarMenuBadge`, `SidebarMenuSkeleton`,
+`SidebarMenuSub` / `SidebarMenuSubButton`, `SidebarGroupAction`,
+`SidebarSeparator`, `SidebarRail`.
-### SWR and React Query
+## Controlled sidebar
-You can use the same approach with [SWR](https://swr.vercel.app/) or [react-query](https://tanstack.com/query/latest/docs/framework/react/overview).
-
-```tsx showLineNumbers title="SWR"
-function NavProjects() {
- const { data, isLoading } = useSWR("/api/projects", fetcher)
-
- if (isLoading) {
- return (
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
- ))}
-
- )
- }
-
- if (!data) {
- return ...
- }
-
- return (
-
- {data.map((project) => (
-
-
-
-
- {project.name}
-
-
-
- ))}
-
- )
-}
-```
-
-```tsx showLineNumbers title="React Query"
-function NavProjects() {
- const { data, isLoading } = useQuery()
-
- if (isLoading) {
- return (
-
- {Array.from({ length: 5 }).map((_, index) => (
-
-
-
- ))}
-
- )
- }
-
- if (!data) {
- return ...
- }
-
- return (
-
- {data.map((project) => (
-
-
-
-
- {project.name}
-
-
-
- ))}
-
- )
-}
-```
-
-## Controlled Sidebar
-
-Use the `open` and `onOpenChange` props to control the sidebar.
-
-```tsx showLineNumbers
+```tsx
export function AppSidebar() {
- const [open, setOpen] = React.useState(false)
+ const [open, setOpen] = React.useState(true)
return (
@@ -1102,43 +211,9 @@ export function AppSidebar() {
}
```
-## Theming
+## Styling with state
-We use the following CSS variables to theme the sidebar.
-
-```css
-@layer base {
- :root {
- --sidebar-background: 0 0% 98%;
- --sidebar-foreground: 240 5.3% 26.1%;
- --sidebar-primary: 240 5.9% 10%;
- --sidebar-primary-foreground: 0 0% 98%;
- --sidebar-accent: 240 4.8% 95.9%;
- --sidebar-accent-foreground: 240 5.9% 10%;
- --sidebar-border: 220 13% 91%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-
- .dark {
- --sidebar-background: 240 5.9% 10%;
- --sidebar-foreground: 240 4.8% 95.9%;
- --sidebar-primary: 0 0% 98%;
- --sidebar-primary-foreground: 240 5.9% 10%;
- --sidebar-accent: 240 3.7% 15.9%;
- --sidebar-accent-foreground: 240 4.8% 95.9%;
- --sidebar-border: 240 3.7% 15.9%;
- --sidebar-ring: 217.2 91.2% 59.8%;
- }
-}
-```
-
-**We intentionally use different variables for the sidebar and the rest of the application** to make it easy to have a sidebar that is styled differently from the rest of the application. Think a sidebar with a darker shade from the main application.
-
-## Styling
-
-Here are some tips for styling the sidebar based on different states.
-
-- **Styling an element based on the sidebar collapsible state.** The following will hide the `SidebarGroup` when the sidebar is in `icon` mode.
+Hide a group when the sidebar is icon-collapsed:
```tsx
@@ -1148,7 +223,7 @@ Here are some tips for styling the sidebar based on different states.
```
-- **Styling a menu action based on the menu button active state.** The following will force the menu action to be visible when the menu button is active.
+Keep a menu action visible when its button is active:
```tsx
@@ -1157,42 +232,6 @@ Here are some tips for styling the sidebar based on different states.
```
-You can find more tips on using states for styling in this [Twitter thread](https://x.com/shadcn/status/1842329158879420864).
+## Source
-## Changelog
-
-### 2024-10-30 Cookie handling in setOpen
-
-- [#5593](https://github.com/shadcn-ui/ui/pull/5593) - Improved setOpen callback logic in ``.
-
-Update the `setOpen` callback in `` as follows:
-
-```tsx showLineNumbers
-const setOpen = React.useCallback(
- (value: boolean | ((value: boolean) => boolean)) => {
- const openState = typeof value === 'function' ? value(open) : value
- if (setOpenProp) {
- setOpenProp(openState)
- } else {
- _setOpen(openState)
- }
-
- // This sets the cookie to keep the sidebar state.
- document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
- },
- [setOpenProp, open]
-)
-```
-
-### 2024-10-21 Fixed `text-sidebar-foreground`
-
-- [#5491](https://github.com/shadcn-ui/ui/pull/5491) - Moved `text-sidebar-foreground` from `` to `` component.
-
-### 2024-10-20 Typo in `useSidebar` hook.
-
-Fixed typo in `useSidebar` hook.
-
-```diff showLineNumbers title="sidebar.tsx"
-- throw new Error("useSidebar must be used within a Sidebar.")
-+ throw new Error("useSidebar must be used within a SidebarProvider.")
-```
+`packages/ui/src/components/shadcn/ui/sidebar.tsx`
diff --git a/apps/design-system/registry/default/example/sidebar-demo.tsx b/apps/design-system/registry/default/example/sidebar-demo.tsx
new file mode 100644
index 00000000000..d8d8a14450b
--- /dev/null
+++ b/apps/design-system/registry/default/example/sidebar-demo.tsx
@@ -0,0 +1,69 @@
+'use client'
+
+import { Calendar, Home, Inbox, Search, Settings } from 'lucide-react'
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
+} from 'ui'
+
+const items = [
+ { title: 'Home', url: '#', icon: Home },
+ { title: 'Inbox', url: '#', icon: Inbox },
+ { title: 'Calendar', url: '#', icon: Calendar },
+ { title: 'Search', url: '#', icon: Search },
+ { title: 'Settings', url: '#', icon: Settings },
+]
+
+const menuButtonClassName =
+ '[&>svg]:size-4 text-foreground-muted data-[active=true]:text-foreground'
+
+export default function SidebarDemo() {
+ return (
+