package showcase
import "github.com/templui/templui/components/sidebar"
import "github.com/templui/templui/components/icon"
import "github.com/templui/templui/components/collapsible"
import "github.com/templui/templui/components/dropdown"
import "github.com/templui/templui/components/avatar"
templ SidebarDefault() {
@sidebar.Layout() {
@sidebar.Sidebar(sidebar.Props{
Collapsible: sidebar.CollapsibleIcon,
Variant: sidebar.VariantFloating,
}) {
@sidebar.Header() {
@sidebar.Menu() {
@sidebar.MenuItem() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Href: "#",
}) {
@icon.LayoutPanelLeft(icon.Props{Class: "size-4"})
<span class="font-semibold">Acme Inc</span>
}
}
}
}
@sidebar.Content() {
@sidebar.Group() {
@sidebar.GroupLabel() {
Platform
}
@sidebar.Menu() {
@sidebar.MenuItem() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Href: "#",
Tooltip: "Home",
}) {
@icon.House(icon.Props{Class: "size-4"})
<span>Home</span>
}
}
@sidebar.MenuItem() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Href: "#",
Tooltip: "Inbox",
}) {
@icon.Inbox(icon.Props{Class: "size-4"})
<span>Inbox</span>
@sidebar.MenuBadge() {
3
}
}
}
@sidebar.MenuItem() {
@collapsible.Collapsible(collapsible.Props{
Open: true,
Class: "group/collapsible w-full",
}) {
@collapsible.Trigger() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Tooltip: "Documents",
}) {
@icon.FileText(icon.Props{Class: "size-4"})
<span>Documents</span>
@icon.ChevronRight(icon.Props{
Class: "ml-auto size-4 transition-transform group-data-[tui-collapsible-state=open]/collapsible:rotate-90",
})
}
}
@collapsible.Content() {
@sidebar.MenuSub() {
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Reports</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Invoices</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
IsActive: true,
}) {
<span>Receipts</span>
}
}
}
}
}
}
}
}
@sidebar.Separator()
@sidebar.Group() {
@sidebar.GroupLabel() {
Projects
}
@sidebar.Menu() {
@sidebar.MenuItem() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Href: "#",
Tooltip: "Project Alpha",
}) {
@icon.Folder(icon.Props{Class: "size-4"})
<span>Project Alpha</span>
@sidebar.MenuBadge() {
12
}
}
}
@sidebar.MenuItem() {
<div class="w-full">
@sidebar.MenuButton(sidebar.MenuButtonProps{
Tooltip: "Project Beta",
}) {
@icon.Folder(icon.Props{Class: "size-4"})
<span>Project Beta</span>
}
@sidebar.MenuSub() {
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Overview</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Analytics</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Reports</span>
}
}
}
</div>
}
}
}
@sidebar.Separator()
@sidebar.Group() {
@sidebar.Menu() {
@sidebar.MenuItem() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Href: "#",
Tooltip: "Team",
}) {
@icon.Users(icon.Props{Class: "size-4"})
<span>Team</span>
}
}
@sidebar.MenuItem() {
@collapsible.Collapsible(collapsible.Props{
Open: false,
Class: "group/collapsible w-full",
}) {
@collapsible.Trigger() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Tooltip: "Settings",
}) {
@icon.Settings(icon.Props{Class: "size-4"})
<span>Settings</span>
@icon.ChevronRight(icon.Props{
Class: "ml-auto size-4 transition-transform group-data-[tui-collapsible-state=open]/collapsible:rotate-90",
})
}
}
@collapsible.Content() {
@sidebar.MenuSub() {
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>General</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Security</span>
}
}
@sidebar.MenuSubItem() {
@sidebar.MenuSubButton(sidebar.MenuSubButtonProps{
Href: "#",
}) {
<span>Notifications</span>
}
}
}
}
}
}
}
}
}
@sidebar.Footer() {
@sidebar.Menu() {
@sidebar.MenuItem() {
@dropdown.Dropdown() {
@dropdown.Trigger() {
@sidebar.MenuButton(sidebar.MenuButtonProps{
Size: sidebar.MenuButtonSizeLg,
}) {
@avatar.Avatar(avatar.Props{Class: "size-8 rounded-lg"}) {
@avatar.Image(avatar.ImageProps{
Src: "https://avatars.githubusercontent.com/u/26936893?v=4",
})
}
<div class="grid flex-1 text-left text-sm leading-tight">
<span class="truncate font-medium">John Doe</span>
<span class="truncate text-xs">[email protected]</span>
</div>
@icon.ChevronsUpDown(icon.Props{Class: "ml-auto size-4"})
}
}
@dropdown.Content(dropdown.ContentProps{
Class: "w-56",
Placement: dropdown.PlacementTopStart,
}) {
@dropdown.Label() {
John Doe
}
@dropdown.Separator()
@dropdown.Item() {
<span class="flex items-center">
@icon.User(icon.Props{Class: "size-4 mr-2"})
Profile
</span>
}
@dropdown.Item() {
<span class="flex items-center">
@icon.Settings(icon.Props{Class: "size-4 mr-2"})
Settings
</span>
}
@dropdown.Separator()
@dropdown.Item() {
<span class="flex items-center">
@icon.LogOut(icon.Props{Class: "size-4 mr-2"})
Log out
</span>
}
}
}
}
}
}
}
@sidebar.Inset() {
<div class="flex h-full flex-col">
<div class="flex h-14 items-center gap-4 px-6">
@sidebar.Trigger()
<span class="text-sm text-muted-foreground">Press Ctrl+B to toggle sidebar</span>
</div>
<div class="flex-1 p-6"></div>
</div>
}
}
}
Installation
Persisted State
The sidebar supports persisting its state across page reloads using cookies. When the sidebar state changes, a cookie named sidebar_state is set with the current open/closed state ("true" for expanded or "false" for collapsed).
To persist sidebar state in your Go application and avoid flicker on page loads, read the cookie server-side and set the Collapsed prop:
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Read sidebar state from cookie
cookie, _ := r.Cookie("sidebar_state")
// "false" means collapsed, "true" or no cookie means expanded
collapsed := cookie != nil && cookie.Value == "false"
// Pass to template
component := MyPage(collapsed)
component.Render(context.Background(), w)
}
templ MyPage(collapsed bool) {
@sidebar.Layout() {
@sidebar.Sidebar(sidebar.Props{
Collapsed: collapsed,
}) {
// Sidebar content
}
@sidebar.Inset() {
// Main content
}
}
}Note: The JavaScript will not automatically restore the state from cookies to prevent flicker. Always read the cookie server-side for the best user experience.
Custom Width
The sidebar width is controlled via the CSS variable --sidebar-width (default: 16rem). You can set it to any valid CSS value using the Attributes prop.
Example
@sidebar.Sidebar(sidebar.Props{
Attributes: templ.Attributes{
"style": "--sidebar-width:20rem", // Any CSS unit works: px, %, vw, etc.
},
}) {
// sidebar content
}Note: You can use any valid CSS unit (rem, px, %, vw) or even dynamic values like calc(), min(), or clamp() for responsive designs.
API Reference
Inset
Main content wrapper that adapts to sidebar state.
| Name | Type | Default |
|---|---|---|
Unique identifier for the inset element. | | - |
Additional CSS classes to apply to the inset. | | - |
Additional HTML attributes to apply to the inset element. | | - |
Group
Groups related menu items together with optional label.
| Name | Type | Default |
|---|---|---|
Unique identifier for the group element. | | - |
Additional CSS classes to apply to the group. | | - |
Additional HTML attributes to apply to the group element. | | - |
GroupLabel
Label for a sidebar group, typically used to name sections like 'Platform' or 'Projects'.
| Name | Type | Default |
|---|---|---|
Unique identifier for the group label element. | | - |
Additional CSS classes to apply to the group label. | | - |
Additional HTML attributes to apply to the group label element. | | - |
Separator
Visual separator between sidebar sections.
| Name | Type | Default |
|---|---|---|
Unique identifier for the separator element. | | - |
Additional CSS classes to apply to the separator. | | - |
Additional HTML attributes to apply to the separator element. | | - |