---
title: Sidebar
description: A composable, themeable and customizable sidebar component.
---

```templ
package examples

import (
	"strconv"

	"github.com/axadrn/shadcn-templ/v2/components/avatar"
	"github.com/axadrn/shadcn-templ/v2/components/collapsible"
	"github.com/axadrn/shadcn-templ/v2/components/dropdownmenu"
	"github.com/axadrn/shadcn-templ/v2/components/icon"
	"github.com/axadrn/shadcn-templ/v2/components/sidebar"
)

// This is sample data.
type sidebarDemoTeam struct {
	Name string
	Logo func(...icon.Props) templ.Component
	Plan string
}

type sidebarDemoNavItem struct {
	Title    string
	URL      string
	Icon     func(...icon.Props) templ.Component
	IsActive bool
	Items    []sidebarDemoNavSubItem
}

type sidebarDemoNavSubItem struct {
	Title string
	URL   string
}

type sidebarDemoProject struct {
	Name string
	URL  string
	Icon func(...icon.Props) templ.Component
}

var sidebarDemoUser = struct {
	Name   string
	Email  string
	Avatar string
}{
	Name:   "Axel Adrian",
	Email:  "m@example.com",
	Avatar: "https://github.com/axadrn.png",
}

var sidebarDemoTeams = []sidebarDemoTeam{
	{Name: "Acme Inc", Logo: icon.GalleryVerticalEnd, Plan: "Enterprise"},
	{Name: "Acme Corp.", Logo: icon.AudioWaveform, Plan: "Startup"},
	{Name: "Evil Corp.", Logo: icon.Command, Plan: "Free"},
}

var sidebarDemoNavMain = []sidebarDemoNavItem{
	{
		Title: "Playground", URL: "#", Icon: icon.SquareTerminal, IsActive: true,
		Items: []sidebarDemoNavSubItem{
			{Title: "History", URL: "#"},
			{Title: "Starred", URL: "#"},
			{Title: "Settings", URL: "#"},
		},
	},
	{
		Title: "Models", URL: "#", Icon: icon.Bot,
		Items: []sidebarDemoNavSubItem{
			{Title: "Genesis", URL: "#"},
			{Title: "Explorer", URL: "#"},
			{Title: "Quantum", URL: "#"},
		},
	},
	{
		Title: "Documentation", URL: "#", Icon: icon.BookOpen,
		Items: []sidebarDemoNavSubItem{
			{Title: "Introduction", URL: "#"},
			{Title: "Get Started", URL: "#"},
			{Title: "Tutorials", URL: "#"},
			{Title: "Changelog", URL: "#"},
		},
	},
	{
		Title: "Settings", URL: "#", Icon: icon.Settings2,
		Items: []sidebarDemoNavSubItem{
			{Title: "General", URL: "#"},
			{Title: "Team", URL: "#"},
			{Title: "Billing", URL: "#"},
			{Title: "Limits", URL: "#"},
		},
	},
}

var sidebarDemoProjects = []sidebarDemoProject{
	{Name: "Design Engineering", URL: "#", Icon: icon.Frame},
	{Name: "Sales & Marketing", URL: "#", Icon: icon.ChartPie},
	{Name: "Travel", URL: "#", Icon: icon.Map},
}

templ sidebarDemoTeamSwitcher() {
	@sidebar.Menu() {
		@sidebar.MenuItem() {
			@dropdownmenu.DropdownMenu(dropdownmenu.Props{ID: "sidebar-demo-team-menu"}) {
				@sidebar.MenuButton(sidebar.MenuButtonProps{
					Size:       sidebar.MenuButtonSizeLg,
					Class:      "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
					Attributes: dropdownmenu.Trigger(ctx),
				}) {
					<div class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
						// All logos render up front, the demo script toggles them on
						// team switch (the pendant of React swapping activeTeam.logo).
						for i, team := range sidebarDemoTeams {
							@team.Logo(icon.Props{
								Class: "size-4" + logoHidden(i),
								Attributes: templ.Attributes{"data-team-logo": strconv.Itoa(i)},
							})
						}
					</div>
					<div class="grid flex-1 text-left text-sm leading-tight">
						<span class="truncate font-medium" data-team-name>{ sidebarDemoTeams[0].Name }</span>
						<span class="truncate text-xs" data-team-plan>{ sidebarDemoTeams[0].Plan }</span>
					</div>
					@icon.ChevronsUpDown(icon.Props{Class: "ml-auto"})
				}
				@dropdownmenu.Content(dropdownmenu.ContentProps{
					Class:      "w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg",
					Align:      dropdownmenu.AlignStart,
					Side:       dropdownmenu.SideRight,
					SideOffset: 4,
					Attributes: templ.Attributes{"data-sidebar-demo-menu": "team"},
				}) {
					@dropdownmenu.Group() {
						@dropdownmenu.Label(dropdownmenu.LabelProps{Class: "text-xs text-muted-foreground"}) {
							Teams
						}
						for i, team := range sidebarDemoTeams {
							@dropdownmenu.Item(dropdownmenu.ItemProps{
								Class:      "gap-2 p-2",
								Attributes: templ.Attributes{"data-team-index": strconv.Itoa(i)},
							}) {
								<div class="flex size-6 items-center justify-center rounded-md border">
									@team.Logo(icon.Props{Class: "size-3.5 shrink-0"})
								</div>
								{ team.Name }
								@dropdownmenu.Shortcut() {
									⌘{ strconv.Itoa(i + 1) }
								}
							}
						}
					}
					@dropdownmenu.Separator()
					@dropdownmenu.Group() {
						@dropdownmenu.Item(dropdownmenu.ItemProps{Class: "gap-2 p-2"}) {
							<div class="flex size-6 items-center justify-center rounded-md border bg-transparent">
								@icon.Plus(icon.Props{Class: "size-4"})
							</div>
							<div class="font-medium text-muted-foreground">Add team</div>
						}
					}
				}
			}
		}
	}
}

func logoHidden(i int) string {
	if i == 0 {
		return ""
	}
	return " hidden"
}

templ sidebarDemoNavMainGroup() {
	@sidebar.Group() {
		@sidebar.GroupLabel() {
			Platform
		}
		@sidebar.Menu() {
			for _, item := range sidebarDemoNavMain {
				@collapsible.Collapsible(collapsible.Props{
					Open:  item.IsActive,
					Class: "group/collapsible",
				}) {
					@sidebar.MenuItem() {
						@sidebar.MenuButton(sidebar.MenuButtonProps{
							Tooltip:    item.Title,
							Attributes: collapsible.Trigger(ctx),
						}) {
							@item.Icon()
							<span>{ item.Title }</span>
							@icon.ChevronRight(icon.Props{Class: "ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"})
						}
						@collapsible.Content() {
							@sidebar.MenuSub() {
								for _, subItem := range item.Items {
									@sidebar.MenuSubItem() {
										@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{Href: subItem.URL}) {
											<span>{ subItem.Title }</span>
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

templ sidebarDemoNavProjects() {
	@sidebar.Group(sidebar.GroupProps{Class: "group-data-[collapsible=icon]:hidden"}) {
		@sidebar.GroupLabel() {
			Projects
		}
		@sidebar.Menu() {
			for _, item := range sidebarDemoProjects {
				@sidebar.MenuItem() {
					@sidebar.MenuButton(sidebar.MenuButtonProps{Href: item.URL}) {
						@item.Icon()
						<span>{ item.Name }</span>
					}
					@dropdownmenu.DropdownMenu() {
						@sidebar.MenuAction(sidebar.MenuActionProps{
							ShowOnHover: true,
							Attributes:  dropdownmenu.Trigger(ctx),
						}) {
							@icon.Ellipsis()
							<span class="sr-only">More</span>
						}
						@dropdownmenu.Content(dropdownmenu.ContentProps{
							Class:      "w-48 rounded-lg",
							Side:       dropdownmenu.SideRight,
							Align:      dropdownmenu.AlignStart,
							Attributes: templ.Attributes{"data-sidebar-demo-menu": "project"},
						}) {
							@dropdownmenu.Item() {
								@icon.Folder(icon.Props{Class: "text-muted-foreground"})
								<span>View Project</span>
							}
							@dropdownmenu.Item() {
								@icon.Forward(icon.Props{Class: "text-muted-foreground"})
								<span>Share Project</span>
							}
							@dropdownmenu.Separator()
							@dropdownmenu.Item() {
								@icon.Trash2(icon.Props{Class: "text-muted-foreground"})
								<span>Delete Project</span>
							}
						}
					}
				}
			}
			@sidebar.MenuItem() {
				@sidebar.MenuButton(sidebar.MenuButtonProps{Class: "text-sidebar-foreground/70"}) {
					@icon.Ellipsis(icon.Props{Class: "text-sidebar-foreground/70"})
					<span>More</span>
				}
			}
		}
	}
}

templ sidebarDemoNavUser() {
	@sidebar.Menu() {
		@sidebar.MenuItem() {
			@dropdownmenu.DropdownMenu() {
				@sidebar.MenuButton(sidebar.MenuButtonProps{
					Size:       sidebar.MenuButtonSizeLg,
					Class:      "data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground",
					Attributes: dropdownmenu.Trigger(ctx),
				}) {
					@avatar.Avatar(avatar.Props{Class: "h-8 w-8 rounded-lg"}) {
						@avatar.Image(avatar.ImageProps{Src: sidebarDemoUser.Avatar, Alt: sidebarDemoUser.Name})
						@avatar.Fallback(avatar.FallbackProps{Class: "rounded-lg"}) {
							AA
						}
					}
					<div class="grid flex-1 text-left text-sm leading-tight">
						<span class="truncate font-medium">{ sidebarDemoUser.Name }</span>
						<span class="truncate text-xs">{ sidebarDemoUser.Email }</span>
					</div>
					@icon.ChevronsUpDown(icon.Props{Class: "ml-auto size-4"})
				}
				@dropdownmenu.Content(dropdownmenu.ContentProps{
					Class:      "w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg",
					Side:       dropdownmenu.SideRight,
					Align:      dropdownmenu.AlignEnd,
					SideOffset: 4,
					Attributes: templ.Attributes{"data-sidebar-demo-menu": "user"},
				}) {
					@dropdownmenu.Group() {
						@dropdownmenu.Label(dropdownmenu.LabelProps{Class: "p-0 font-normal"}) {
							<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
								@avatar.Avatar(avatar.Props{Class: "h-8 w-8 rounded-lg"}) {
									@avatar.Image(avatar.ImageProps{Src: sidebarDemoUser.Avatar, Alt: sidebarDemoUser.Name})
									@avatar.Fallback(avatar.FallbackProps{Class: "rounded-lg"}) {
										AA
									}
								}
								<div class="grid flex-1 text-left text-sm leading-tight">
									<span class="truncate font-medium">{ sidebarDemoUser.Name }</span>
									<span class="truncate text-xs">{ sidebarDemoUser.Email }</span>
								</div>
							</div>
						}
					}
					@dropdownmenu.Separator()
					@dropdownmenu.Group() {
						@dropdownmenu.Item() {
							@icon.Sparkles()
							Upgrade to Pro
						}
					}
					@dropdownmenu.Separator()
					@dropdownmenu.Group() {
						@dropdownmenu.Item() {
							@icon.BadgeCheck()
							Account
						}
						@dropdownmenu.Item() {
							@icon.CreditCard()
							Billing
						}
						@dropdownmenu.Item() {
							@icon.Bell()
							Notifications
						}
					}
					@dropdownmenu.Separator()
					@dropdownmenu.Group() {
						@dropdownmenu.Item() {
							@icon.LogOut()
							Log out
						}
					}
				}
			}
		}
	}
}

templ SidebarDemo() {
	@sidebar.Provider() {
		@sidebar.Sidebar(sidebar.Props{Collapsible: sidebar.CollapsibleIcon}) {
			@sidebar.Header() {
				@sidebarDemoTeamSwitcher()
			}
			@sidebar.Content() {
				@sidebarDemoNavMainGroup()
				@sidebarDemoNavProjects()
			}
			@sidebar.Footer() {
				@sidebarDemoNavUser()
			}
			@sidebar.Rail()
		}
		@sidebar.Inset() {
			<header class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
				<div class="flex items-center gap-2 px-4">
					@sidebar.Trigger(sidebar.TriggerProps{Class: "-ml-1"})
				</div>
			</header>
		}
	}
	<script nonce={ templ.GetNonce(ctx) }>
		(() => {
			// Team switching, the pendant of the demo's activeTeam React state.
			document.addEventListener("click", (e) => {
				if (!(e.target instanceof Element)) return;
				const item = e.target.closest("#sidebar-demo-team-menu [data-team-index]");
				if (!item) return;
				const i = item.getAttribute("data-team-index");
				const name = item.textContent.trim().replace(/⌘\d$/, "").trim();
				const plan = ["Enterprise", "Startup", "Free"][parseInt(i, 10)];
				document.querySelectorAll("[data-team-logo]").forEach((logo) => {
					logo.classList.toggle("hidden", logo.getAttribute("data-team-logo") !== i);
				});
				const nameEl = document.querySelector("[data-team-name]");
				const planEl = document.querySelector("[data-team-plan]");
				if (nameEl) nameEl.textContent = name;
				if (planEl) planEl.textContent = plan;
			});

			// side={isMobile ? "bottom" : "right"} pendant: the menus flip to
			// bottom below md, like useSidebar's isMobile.
			const mq = window.matchMedia("(max-width: 767px)");
			const applySides = () => {
				document.querySelectorAll("[data-sidebar-demo-menu]").forEach((menu) => {
					menu.setAttribute("data-tui-dropdownmenu-side", mq.matches ? "bottom" : "right");
					if (menu.getAttribute("data-sidebar-demo-menu") === "project") {
						menu.setAttribute("data-tui-dropdownmenu-align", mq.matches ? "end" : "start");
					}
				});
			};
			applySides();
			mq.addEventListener("change", applySides);
		})();
	</script>
}
```

Sidebars are one of the most complex components to build. They are central
to any application and often contain a lot of moving parts.

We now have a solid foundation to build on top of. Composable. Themeable.
Customizable.

## Installation

<CodeTabs>

<TabsList>
  <TabsTrigger value="cli">Command</TabsTrigger>
  <TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">

```bash
shadcn-templ add sidebar
```

</TabsContent>

<TabsContent value="manual">

<Steps className="mb-0 pt-2">

<Step>Copy and paste the following code into your project.</Step>

<ComponentSource name="sidebar" title="components/sidebar/sidebar.templ" />

<ComponentSource name="sidebar" title="components/sidebar/sidebar.js" />

Component scripts are loaded through the shared script bundle, see [JavaScript](/docs/installation#javascript).

<Step>Update the import paths to match your project setup.</Step>

</Steps>

</TabsContent>

</CodeTabs>

## Usage

```templ showLineNumbers title="layout.templ"
import "github.com/axadrn/shadcn-templ/v2/components/sidebar"

templ Layout() {
	@sidebar.Provider() {
		@AppSidebar()
		<main>
			@sidebar.Trigger()
			{ children... }
		</main>
	}
}
```

```templ showLineNumbers title="app_sidebar.templ"
templ AppSidebar() {
	@sidebar.Sidebar() {
		@sidebar.Header()
		@sidebar.Content() {
			@sidebar.Group()
			@sidebar.Group()
		}
		@sidebar.Footer()
	}
}
```

## Composition

Use the following composition to build a `Sidebar` layout:

```text
sidebar.Provider
├── sidebar.Sidebar
│   ├── sidebar.Header
│   ├── sidebar.Content
│   │   ├── sidebar.Group
│   │   │   ├── sidebar.GroupLabel
│   │   │   ├── sidebar.GroupAction
│   │   │   ├── sidebar.GroupContent
│   │   │   └── sidebar.Menu
│   │   │       ├── sidebar.MenuItem
│   │   │       │   ├── sidebar.MenuButton
│   │   │       │   ├── sidebar.MenuAction
│   │   │       │   └── sidebar.MenuBadge
│   │   │       └── sidebar.MenuItem
│   │   │           ├── sidebar.MenuButton
│   │   │           └── sidebar.MenuSub
│   │   │               ├── sidebar.MenuSubItem
│   │   │               └── sidebar.MenuSubItem
│   │   └── sidebar.Group
│   │       └── sidebar.Menu
│   │           ├── sidebar.MenuItem
│   │           └── sidebar.MenuItem
│   ├── sidebar.Footer
│   └── sidebar.Rail
├── sidebar.Inset
└── sidebar.Trigger
```

## Structure

- **sidebar.Provider** — Handles collapsible state and provides the sidebar context to child components.
- **sidebar.Sidebar** — The main collapsible sidebar panel.
- **sidebar.Header** — Sticky at the top; use for branding, titles, or workspace switchers.
- **sidebar.Footer** — Sticky at the bottom; use for user menus, settings, or actions.
- **sidebar.Content** — Scrollable region between the header and footer.
- **sidebar.Group** — Groups related navigation with optional label, action, and content areas.
- **sidebar.Menu** / **sidebar.MenuItem** — Menu structure for links, badges, actions, and nested submenus.
- **sidebar.Rail** — Resize handle for adjusting sidebar width when applicable.
- **sidebar.Inset** — Wraps main content when using the `inset` variant.
- **sidebar.Trigger** — Control that toggles the sidebar open or collapsed.

<img src="/assets/img/docs-sidebar-structure.png" width="716" height="420" alt="Sidebar Structure" class="mt-6 w-full overflow-hidden rounded-2xl border dark:hidden"/>
<img src="/assets/img/docs-sidebar-structure-dark.png" width="716" height="420" alt="Sidebar Structure" class="mt-6 hidden w-full overflow-hidden rounded-2xl border dark:block"/>

## SidebarProvider

The `sidebar.Provider` component provides the sidebar context to the `sidebar.Sidebar` component. You should always wrap your application in a `sidebar.Provider` component.

### Props

| Name        | Type   | Description                             |
| ----------- | ------ | --------------------------------------- |
| `DisableDefaultOpen` | `bool` | Default collapsed state of the sidebar. |

### Width

If you have a single sidebar in your application, you can use the `sidebarWidth` and `sidebarWidthMobile` constants in `sidebar.templ` to set the width of the sidebar.

```go showLineNumbers title="components/sidebar/sidebar.templ"
sidebarWidth       = "16rem"
sidebarWidthMobile = "18rem"
```

For multiple sidebars in your application, you can use the `--sidebar-width` and `--sidebar-width-mobile` CSS variables in the `style` attribute.

```templ showLineNumbers
@sidebar.Provider(sidebar.ProviderProps{
	Attributes: templ.Attributes{
		"style": "--sidebar-width: 20rem; --sidebar-width-mobile: 20rem;",
	},
}) {
	@sidebar.Sidebar()
}
```

### Keyboard Shortcut

To trigger the sidebar, you use the `cmd+b` keyboard shortcut on Mac and `ctrl+b` on Windows.

```go showLineNumbers title="components/sidebar/sidebar.templ"
sidebarKeyboardShortcut = "b"
```

## Sidebar

The main `sidebar.Sidebar` component used to render a collapsible sidebar.

### Props

| Property      | Type                                                         | Description                       |
| ------------- | ------------------------------------------------------------ | --------------------------------- |
| `Side`        | `SideLeft` or `SideRight`                                    | The side of the sidebar.          |
| `Variant`     | `VariantSidebar`, `VariantFloating`, or `VariantInset`       | The variant of the sidebar.       |
| `Collapsible` | `CollapsibleOffcanvas`, `CollapsibleIcon`, or `CollapsibleNone` | Collapsible state of the sidebar. |

| Collapsible | 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.                                   |

<Callout>
**Note:** If you use the `inset` variant, remember to wrap your main content
in a `sidebar.Inset` component.
</Callout>

```templ showLineNumbers
@sidebar.Provider() {
	@sidebar.Sidebar(sidebar.Props{Variant: sidebar.VariantInset})
	@sidebar.Inset() {
		<main>{ children... }</main>
	}
}
```

## useSidebar

The `window.tui.sidebar` API is the `useSidebar` pendant and is used to control the sidebar.

```js showLineNumbers
const {
	state,
	open,
	setOpen,
	openMobile,
	setOpenMobile,
	isMobile,
	toggleSidebar,
} = window.tui.sidebar
```

| Property        | Type                        | Description                                   |
| --------------- | --------------------------- | --------------------------------------------- |
| `state`         | `"expanded" \| "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.      |

For multiple sidebars, every function optionally takes the sidebar ID as its last argument, e.g. `toggleSidebar("left-nav")`. Without an ID the first sidebar on the page is addressed.

## SidebarHeader

Use the `sidebar.Header` component to add a sticky header to the sidebar.

```templ showLineNumbers title="app_sidebar.templ"
@sidebar.Sidebar() {
	@sidebar.Header() {
		@sidebar.Menu() {
			@sidebar.MenuItem() {
				@dropdownmenu.DropdownMenu() {
					@sidebar.MenuButton(sidebar.MenuButtonProps{
						Attributes: dropdownmenu.Trigger(ctx),
					}) {
						Select Workspace
						@icon.ChevronDown(icon.Props{Class: "ml-auto"})
					}
					@dropdownmenu.Content() {
						@dropdownmenu.Item() {
							<span>Acme Inc</span>
						}
					}
				}
			}
		}
	}
}
```

## SidebarFooter

Use the `sidebar.Footer` component to add a sticky footer to the sidebar.

```templ showLineNumbers
@sidebar.Sidebar() {
	@sidebar.Footer() {
		@sidebar.Menu() {
			@sidebar.MenuItem() {
				@sidebar.MenuButton() {
					@icon.User() Username
				}
			}
		}
	}
}
```

## SidebarContent

The `sidebar.Content` component is used to wrap the content of the sidebar. This is where you add your `sidebar.Group` components. It is scrollable.

```templ showLineNumbers
@sidebar.Sidebar() {
	@sidebar.Content() {
		@sidebar.Group()
		@sidebar.Group()
	}
}
```

## SidebarGroup

Use the `sidebar.Group` component to create a section within the sidebar.

A `sidebar.Group` has a `sidebar.GroupLabel`, a `sidebar.GroupContent` and an optional `sidebar.GroupAction`.

```templ showLineNumbers
@sidebar.Group() {
	@sidebar.GroupLabel() {
		Application
	}
	@sidebar.GroupAction() {
		@icon.Plus()
		<span class="sr-only">Add Project</span>
	}
	@sidebar.GroupContent()
}
```

To make a `sidebar.Group` collapsible, wrap it in a `Collapsible`.

```templ showLineNumbers
@collapsible.Collapsible(collapsible.Props{Open: true, Class: "group/collapsible"}) {
	@sidebar.Group() {
		@sidebar.GroupLabel(sidebar.GroupLabelProps{
			Attributes: collapsible.Trigger(ctx),
		}) {
			Help
			@icon.ChevronDown(icon.Props{Class: "ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180"})
		}
		@collapsible.Content() {
			@sidebar.GroupContent()
		}
	}
}
```

## SidebarMenu

The `sidebar.Menu` component is used for building a menu within a `sidebar.Group`.

<img src="/assets/img/docs-sidebar-menu.png" width="716" height="420" alt="Sidebar Menu" class="mt-6 w-full overflow-hidden rounded-2xl border dark:hidden"/>
<img src="/assets/img/docs-sidebar-menu-dark.png" width="716" height="420" alt="Sidebar Menu" class="mt-6 hidden w-full overflow-hidden rounded-2xl border dark:block"/>

```templ showLineNumbers
@sidebar.Menu() {
	for _, project := range projects {
		@sidebar.MenuItem() {
			@sidebar.MenuButton(sidebar.MenuButtonProps{Href: project.URL}) {
				@project.Icon()
				<span>{ project.Name }</span>
			}
		}
	}
}
```

## SidebarMenuButton

The `sidebar.MenuButton` component is used to render a menu button within a `sidebar.MenuItem`.

By default, the `sidebar.MenuButton` renders a button. Set the `Href` prop to render an `a` tag instead.

Use the `IsActive` prop to mark a menu item as active.

```templ showLineNumbers
@sidebar.MenuButton(sidebar.MenuButtonProps{Href: "#", IsActive: true}) {
	Home
}
```

## SidebarMenuAction

The `sidebar.MenuAction` component is used to render a menu action within a `sidebar.MenuItem`.

```templ showLineNumbers
@sidebar.MenuItem() {
	@sidebar.MenuButton(sidebar.MenuButtonProps{Href: "#"}) {
		@icon.House()
		<span>Home</span>
	}
	@sidebar.MenuAction() {
		@icon.Plus()
		<span class="sr-only">Add Project</span>
	}
}
```

## SidebarMenuSub

The `sidebar.MenuSub` component is used to render a submenu within a `sidebar.Menu`.

```templ showLineNumbers
@sidebar.MenuItem() {
	@sidebar.MenuButton()
	@sidebar.MenuSub() {
		@sidebar.MenuSubItem() {
			@sidebar.MenuSubButton()
		}
	}
}
```

## SidebarMenuBadge

The `sidebar.MenuBadge` component is used to render a badge within a `sidebar.MenuItem`.

```templ showLineNumbers
@sidebar.MenuItem() {
	@sidebar.MenuButton()
	@sidebar.MenuBadge() {
		24
	}
}
```

## SidebarMenuSkeleton

The `sidebar.MenuSkeleton` component is used to render a skeleton for a `sidebar.Menu`.

```templ showLineNumbers
@sidebar.Menu() {
	for range 5 {
		@sidebar.MenuItem() {
			@sidebar.MenuSkeleton()
		}
	}
}
```

## SidebarTrigger

Use the `sidebar.Trigger` component to render a button that toggles the sidebar.

```templ showLineNumbers
<button onclick="window.tui.sidebar.toggleSidebar()">Toggle Sidebar</button>
```

## SidebarRail

The `sidebar.Rail` component is used to render a rail within a `sidebar.Sidebar`. This rail can be used to toggle the sidebar.

```templ showLineNumbers
@sidebar.Sidebar() {
	@sidebar.Header()
	@sidebar.Content() {
		@sidebar.Group()
	}
	@sidebar.Footer()
	@sidebar.Rail()
}
```

## Controlled Sidebar

Use the `DisableDefaultOpen` prop on `sidebar.Provider` to render the sidebar collapsed on the server. The `sidebar_state` cookie carries the last state, so the server can render the sidebar the way the user left it.

```templ showLineNumbers
@sidebar.Provider(sidebar.ProviderProps{DisableDefaultOpen: collapsedFromCookie})
```

```go showLineNumbers
collapsed := false
if c, err := r.Cookie("sidebar_state"); err == nil {
	collapsed = c.Value == "false"
}
```

## Theming

We use the following CSS variables to theme the sidebar.

```css
:root {
	--sidebar: oklch(0.985 0 0);
	--sidebar-foreground: oklch(0% 0 0);
	--sidebar-primary: oklch(0.205 0 0);
	--sidebar-primary-foreground: oklch(0.985 0 0);
	--sidebar-accent: oklch(0.97 0 0);
	--sidebar-accent-foreground: oklch(0.205 0 0);
	--sidebar-border: oklch(0.922 0 0);
	--sidebar-ring: oklch(0.708 0 0);
}

.dark {
	--sidebar: oklch(0.205 0 0);
	--sidebar-foreground: oklch(0.985 0 0);
	--sidebar-primary: oklch(0.488 0.243 264.376);
	--sidebar-primary-foreground: oklch(0.985 0 0);
	--sidebar-accent: oklch(0.269 0 0);
	--sidebar-accent-foreground: oklch(0.985 0 0);
	--sidebar-border: oklch(1 0 0 / 10%);
	--sidebar-ring: oklch(0.439 0 0);
}
```

## Styling

Here are some tips for styling the sidebar based on different states.

```templ
@sidebar.Sidebar(sidebar.Props{Collapsible: sidebar.CollapsibleIcon}) {
	@sidebar.Content() {
		@sidebar.Group(sidebar.GroupProps{Class: "group-data-[collapsible=icon]:hidden"})
	}
}
```

```templ
@sidebar.MenuItem() {
	@sidebar.MenuButton()
	@sidebar.MenuAction(sidebar.MenuActionProps{Class: "peer-data-[active=true]/menu-button:opacity-100"})
}
```
