Sidebar
A collapsible, grouped navigation rail with labelled sections, a current-item accent, and badges folded into each item's accessible name.
Preview
<script lang="ts">
import { Sidebar, SidebarGroup, SidebarItem } from "fancy-ui-svelte";
let current = $state("dashboard");
</script>
<Sidebar>
<SidebarGroup label="General">
<SidebarItem
href="#dashboard"
current={current === "dashboard"}
onclick={() => (current = "dashboard")}
>
Dashboard
</SidebarItem>
<SidebarItem
href="#projects"
current={current === "projects"}
onclick={() => (current = "projects")}
>
Projects
</SidebarItem>
<SidebarItem
href="#settings"
current={current === "settings"}
onclick={() => (current = "settings")}
>
Settings
</SidebarItem>
</SidebarGroup>
</Sidebar>Installation
Usage
<script lang="ts">
import { Sidebar } from 'fancy-ui-svelte';
</script>
<Sidebar />Examples
Basic Usage
<script lang="ts">
import { Sidebar, SidebarGroup, SidebarItem } from "$lib/fancy-ui/sidebar";
let current = $state("dashboard");
</script>
<Sidebar>
<SidebarGroup label="General">
<SidebarItem
href="#dashboard"
current={current === "dashboard"}
onclick={() => (current = "dashboard")}
>
Dashboard
</SidebarItem>
<SidebarItem
href="#projects"
current={current === "projects"}
onclick={() => (current = "projects")}
>
Projects
</SidebarItem>
<SidebarItem
href="#settings"
current={current === "settings"}
onclick={() => (current = "settings")}
>
Settings
</SidebarItem>
</SidebarGroup>
</Sidebar>With Badges
A count folded into an item's accessible name, and a second grouped section.
<script lang="ts">
import { Sidebar, SidebarGroup, SidebarItem, SidebarSeparator } from "$lib/fancy-ui/sidebar";
</script>
<Sidebar>
<SidebarGroup label="General">
<SidebarItem href="#dashboard" current>Dashboard</SidebarItem>
<SidebarItem href="#projects">Projects</SidebarItem>
<SidebarItem href="#inbox" badge={4} badgeLabel="unread">Inbox</SidebarItem>
<SidebarItem href="#settings">Settings</SidebarItem>
</SidebarGroup>
<SidebarSeparator />
<SidebarGroup label="Danger zone">
<SidebarItem href="#archived" badge="12">Archived</SidebarItem>
</SidebarGroup>
</Sidebar>Collapsed
Icon-only rail — labels and badge meaning move to sr-only text, never removed.
<script lang="ts">
import { Sidebar, SidebarGroup, SidebarItem } from "$lib/fancy-ui/sidebar";
let collapsed = $state(true);
</script>
{#snippet dashboardIcon()}
<svg
class="size-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="3" y="3" width="7" height="9" rx="1" />
<rect x="14" y="3" width="7" height="5" rx="1" />
<rect x="14" y="12" width="7" height="9" rx="1" />
<rect x="3" y="16" width="7" height="5" rx="1" />
</svg>
{/snippet}
{#snippet inboxIcon()}
<svg
class="size-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 12h-6l-2 3h-4l-2-3H2" />
<path
d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11Z"
/>
</svg>
{/snippet}
{#snippet settingsIcon()}
<svg
class="size-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="3" />
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z"
/>
</svg>
{/snippet}
<div class="flex flex-col gap-3">
<button
type="button"
class="border-border text-foreground w-fit rounded-[6px] border px-3 py-1.5 text-xs font-medium"
onclick={() => (collapsed = !collapsed)}
>
{collapsed ? "Expand" : "Collapse"}
</button>
<Sidebar {collapsed}>
<SidebarGroup label="General">
<SidebarItem href="#dashboard" current icon={dashboardIcon}>Dashboard</SidebarItem>
<SidebarItem href="#inbox" badge={4} badgeLabel="unread" icon={inboxIcon}>Inbox</SidebarItem>
<SidebarItem href="#settings" icon={settingsIcon}>Settings</SidebarItem>
</SidebarGroup>
</Sidebar>
</div>With Footer
An avatar and name row, above a separator, pinned to the bottom.
<script lang="ts">
import { Sidebar, SidebarGroup, SidebarItem, SidebarFooter } from "$lib/fancy-ui/sidebar";
</script>
<Sidebar>
<SidebarGroup label="General">
<SidebarItem href="#dashboard" current>Dashboard</SidebarItem>
<SidebarItem href="#projects">Projects</SidebarItem>
<SidebarItem href="#settings">Settings</SidebarItem>
</SidebarGroup>
<SidebarFooter>
{#snippet avatar()}
<span
class="inline-block h-[22px] w-[22px] rounded-full bg-gradient-to-br from-purple-500 to-cyan-400"
></span>
{/snippet}
Rama H.
</SidebarFooter>
</Sidebar>Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | "Sidebar" | Accessible name for the nav landmark. |
collapsed | boolean | false | Whether the sidebar is collapsed to an icon-only rail. Plain, not bindable — nothing inside the compound ever changes it itself. |
class | string | - | Additional CSS classes, merged onto the root. |
ref | HTMLElement | null | null | Bindable reference to the root nav element. |
SidebarGroup.label * | string | - | The section heading, e.g. "General". |
SidebarGroup.class | string | - | Additional CSS classes, merged onto the group. |
SidebarItem.href | string | - | Renders an <a> when set; a <button type="button"> otherwise. |
SidebarItem.current | boolean | false | Marks this as the current item — aria-current="page" plus the accent left bar. |
SidebarItem.badge | string | number | - | A count or short flag, e.g. 4, rendered as a pill. |
SidebarItem.badgeLabel | string | - | What the badge means, folded into the accessible name. Defaults to just the badge value. |
SidebarItem.disabled | boolean | false | Disables both the click and keyboard-activation paths. |
SidebarItem.onclick | (event: MouseEvent) => void | - | Native click handler, for the button branch. Never called while disabled. |
SidebarItem.class | string | - | Additional CSS classes, merged onto the item. |
SidebarItem.ref | HTMLAnchorElement | HTMLButtonElement | null | null | Bindable reference to the item's interactive element. |
SidebarSeparator.class | string | - | Additional CSS classes, merged onto the hairline. |
SidebarSeparator.ref | HTMLHRElement | null | null | Bindable reference to the <hr> element. |
SidebarFooter.class | string | - | Additional CSS classes, merged onto the footer row. |
SidebarFooter.ref | HTMLDivElement | null | null | Bindable reference to the footer's root element. |
Slots
| Slot | Description |
|---|---|
children | SidebarGroup, SidebarSeparator and SidebarFooter. |
SidebarGroup.children | The SidebarItems in this section. |
SidebarItem.icon | A decorative glyph or icon, shown even while the sidebar is collapsed. |
SidebarItem.children | The item's label. Moves to sr-only text while collapsed — never removed. |
SidebarFooter.avatar | A decorative avatar, shown even while the sidebar is collapsed. |
SidebarFooter.children | The name / text next to the avatar. sr-only while collapsed. |
Links
Related components
Breadcrumb
A data-driven trail of links with automatic truncation, a decorative separator, and the current page marked for assistive tech.
CommandMenu
A modal search palette over a flat vocabulary of items — grouped results, diacritic-insensitive filtering, match highlighting, and full keyboard navigation with focus that never leaves the search field.
ContextMenu
A menu that opens at the pointer on right-click, sharing DropdownMenu's item, submenu and keyboard behaviour but anchored to a virtual point instead of a trigger element.