Design wechseln

Admin-Skeleton mit shadcn/ui: Sidebar + Layout – Best Practices

Schritt für Schritt ein skalierbares Admin-Skeleton – von der Sidebar-Komponente bis zur Next.js-Layout-Integration, mit Code zum direkten Einstieg.


Letzte Woche kam ein Admin-Dashboard-Projekt rein – als Erstes dachte ich an shadcn/ui. Vorher hatte ich Ant Design und MUI genutzt; Styles anzupassen war oft mühsam – entweder massenhaft Overrides oder festgelegte Design-Vorgaben.

shadcn/ui ist anders. Copy-paste: Code liegt in Ihrem Projekt, Änderungen sind jederzeit möglich. Nach zwei Wochen Einsatz überzeugt es – besonders die Sidebar mit dem App Router macht das Admin-Skeleton deutlich übersichtlicher.

Dieser Artikel fasst meine Praxis zusammen: von null ein erweiterbares Admin-Layout aufbauen.


1. Warum shadcn/ui Sidebar?

Zuerst ein paar Erfahrungen aus der Praxis.

Mit Ant Design Pro startet man schnell, aber langfristig wird es eng: Sidebar-Styles erfordern lange Docs-Recherche; individuelle Interaktionen stoßen an Framework-Grenzen. Bei MUI ähnlich – Themes sind flexibel, setzen aber Material Design voraus.

Schwächen klassischer Ansätze

Ant Design: Funktionsreich, hohe Anpassungskosten. Abgerundete Sidebar-Ecken können drei Ebenen CSS-Overrides bedeuten.

MUI: Vollständiges Designsystem, steile Lernkurve. Styled Components – für Team-Neulinge oft eine Woche Einarbeitung.

Selbst bauen: Volle Kontrolle, aber eine responsive Sidebar mit Barrierefreiheit und Tastaturnavigation? Realistisch mehrere Tage.

Der shadcn/ui-Ansatz

shadcn/ui geht einen anderen Weg:

  • Copy-paste-Modell: Komponentencode im Projekt, keine Black-Box-Abhängigkeit
  • Radix UI als Basis: Barrierefreiheit eingebaut – Tastatur, ARIA
  • Tailwind CSS: Styles sind Klassen – anpassen ohne Override-Kampf

Viele Teams wechseln von MUI zu shadcn/ui, weil sie Kontrolle wollen, nicht ein fertiges Template.

Wann es passt

Wenn Sie bauen:

  • mittelgroße Admin-Dashboards
  • SaaS-Konsolen
  • interne Tools oder Ops-Plattformen

lohnt sich shadcn/ui Sidebar. Kein komplettes Admin-Template – aber ein flexibles Gerüst.


2. Sidebar-Komponentenarchitektur

Vor dem Codieren: das Komponentensystem von shadcn/ui Sidebar. Die offizielle Doku ist hier klar – kurz zusammengefasst.

Kernkomponenten

Die Sidebar besteht aus mehreren Teilen:

SidebarProvider   // State-Kontext, umschließt die App
Sidebar          // Sidebar-Container
SidebarHeader    // Fixer Kopfbereich, z. B. Logo
SidebarContent   // Scrollbarer Bereich für Menüs
SidebarGroup     // Menügruppe
SidebarMenu      // Menüliste
SidebarMenuItem  // Menüeintrag
SidebarMenuButton // Menübutton (unterstützt Link)
SidebarFooter    // Fixer Fußbereich, z. B. User-Info
SidebarTrigger   // Ein-/Ausklappen
SidebarInset     // Wrapper für Hauptinhalt

Die Hierarchie:

SidebarProvider
├── Sidebar
│   ├── SidebarHeader
│   ├── SidebarContent
│   │   └── SidebarGroup
│   │       └── SidebarMenu
│   │           └── SidebarMenuItem
│   │               └── SidebarMenuButton
│   └── SidebarFooter
└── SidebarInset
    └── {children}

State-Management

Der Einklapp-Status liegt beim SidebarProvider. Zwei Modi:

Unkontrolliert (empfohlen):

<SidebarProvider defaultOpen={true}>
  <Sidebar />
</SidebarProvider>

Kontrolliert:

const [open, setOpen] = useState(true);

<SidebarProvider open={open} onOpenChange={setOpen}>
  <Sidebar />
</SidebarProvider>

Meist reicht unkontrolliert. Kontrolliert nur, wenn Sie den State woanders steuern (z. B. in den Einstellungen).

Responsives Verhalten

Eingebaute Responsive-Logik:

  • Desktop: Sidebar links fixiert, per SidebarTrigger einklappbar
  • Mobil: automatisch Sheet-Drawer, Trigger öffnet Overlay

Intern in der Komponente – SidebarProvider konfigurieren, Rest erledigt die Sidebar.


3. Next.js Layout – Praxis

Konzept geklärt – jetzt Sidebar ins Next.js-Layout-System einbinden.

3.1 Projektstruktur

Empfehlung: Route Groups für unterschiedliche Layouts ohne URL-Änderung.

app/
├── layout.tsx              # Root Layout (global)
├── (marketing)/            # Marketing (Landing, About)
│   ├── layout.tsx          # ohne Sidebar
│   └── page.tsx            # Startseite
├── (dashboard)/            # Dashboard-Gruppe
│   ├── layout.tsx          # Layout mit Sidebar
│   ├── page.tsx            # Dashboard-Start
│   ├── users/
│   │   └── page.tsx        # Benutzerverwaltung
│   └── settings/
│       └── page.tsx        # Einstellungen
└── (auth)/                 # Auth-Gruppe
    ├── layout.tsx          # zentriertes Layout
    ├── login/
    │   └── page.tsx        # Login
    └── register/
        └── page.tsx        # Registrierung

Vorteile:

  1. Layout-Trennung: Marketing ohne Sidebar, Dashboard mit – natürlich getrennt
  2. Schlanke URLs: (dashboard) erscheint nicht – /users bleibt /users
  3. Erweiterbar: neue Gruppe = neuer Ordner

3.2 Root Layout

Einstiegspunkt der App – global: Theme, Schrift, SidebarProvider.

// app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { SidebarProvider } from "@/components/ui/sidebar";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Admin Dashboard",
  description: "Built with shadcn/ui and Next.js",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang="de"&gt;
      &lt;body className={inter.className}&gt;
        &lt;SidebarProvider&gt;
          {children}
        &lt;/SidebarProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}

SidebarProvider im Root Layout, nicht im Dashboard Layout – so bleibt der Einklapp-Status beim Wechsel von /users zu /settings erhalten.

3.3 Dashboard Layout

Kernlayout des Backends – hier kommt die Sidebar rein.

// app/(dashboard)/layout.tsx
import { AppSidebar } from "@/components/app-sidebar";
import { SidebarInset, SidebarTrigger } from "@/components/ui/sidebar";
import { Separator } from "@/components/ui/separator";
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;&gt;
      &lt;AppSidebar /&gt;
      &lt;SidebarInset&gt;
        &lt;header className="flex h-16 shrink-0 items-center gap-2 border-b px-4"&gt;
          &lt;SidebarTrigger className="-ml-1" /&gt;
          &lt;Separator orientation="vertical" className="mr-2 h-4" /&gt;
          &lt;Breadcrumb&gt;
            &lt;BreadcrumbList&gt;
              &lt;BreadcrumbItem className="hidden md:block"&gt;
                &lt;BreadcrumbLink href="/dashboard"&gt;
                  Admin
                &lt;/BreadcrumbLink&gt;
              &lt;/BreadcrumbItem&gt;
              &lt;BreadcrumbSeparator className="hidden md:block" /&gt;
              &lt;BreadcrumbItem&gt;
                &lt;BreadcrumbPage&gt;Übersicht&lt;/BreadcrumbPage&gt;
              &lt;/BreadcrumbItem&gt;
            &lt;/BreadcrumbList&gt;
          &lt;/Breadcrumb&gt;
        &lt;/header&gt;
        &lt;main className="flex-1 p-4 pt-6"&gt;{children}&lt;/main&gt;
      &lt;/SidebarInset&gt;
    &lt;/&gt;
  );
}

Enthalten:

  1. AppSidebar: eigene Sidebar (nächster Abschnitt)
  2. SidebarInset: Hauptbereich, passt Breite beim Einklappen an
  3. Header: Trigger + Breadcrumb
  4. Main: Seiteninhalt

3.4 AppSidebar implementieren

Konfigurationsgesteuert: Navigation in einer Config-Datei, Komponente rendert daraus.

Navigation definieren:

// lib/navigation.ts
import {
  Home,
  Users,
  Settings,
  FileText,
  BarChart3,
  Shield,
} from "lucide-react";

export interface NavItem {
  title: string;
  href: string;
  icon: React.ComponentType&lt;{ className?: string }&gt;;
  badge?: string;
}

export const navConfig: NavItem[] = [
  {
    title: "Übersicht",
    href: "/dashboard",
    icon: Home,
  },
  {
    title: "Benutzer",
    href: "/users",
    icon: Users,
    badge: "12",
  },
  {
    title: "Analytics",
    href: "/analytics",
    icon: BarChart3,
  },
  {
    title: "Inhalte",
    href: "/content",
    icon: FileText,
  },
  {
    title: "Einstellungen",
    href: "/settings",
    icon: Settings,
  },
  {
    title: "Berechtigungen",
    href: "/permissions",
    icon: Shield,
  },
];

AppSidebar:

// components/app-sidebar.tsx
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import {
  Sidebar,
  SidebarContent,
  SidebarFooter,
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarHeader,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/components/ui/sidebar";
import { navConfig } from "@/lib/navigation";
import { Logo } from "@/components/logo";
import { UserNav } from "@/components/user-nav";

export function AppSidebar() {
  const pathname = usePathname();

  return (
    &lt;Sidebar&gt;
      &lt;SidebarHeader className="border-b border-border"&gt;
        &lt;Logo /&gt;
      &lt;/SidebarHeader&gt;

      &lt;SidebarContent&gt;
        &lt;SidebarGroup&gt;
          &lt;SidebarGroupLabel&gt;Navigation&lt;/SidebarGroupLabel&gt;
          &lt;SidebarGroupContent&gt;
            &lt;SidebarMenu&gt;
              {navConfig.map((item) =&gt; {
                const isActive = pathname === item.href;

                return (
                  &lt;SidebarMenuItem key={item.href}&gt;
                    &lt;SidebarMenuButton
                      asChild
                      isActive={isActive}
                      tooltip={item.title}
                    &gt;
                      &lt;Link href={item.href}&gt;
                        &lt;item.icon className="h-4 w-4" /&gt;
                        &lt;span&gt;{item.title}&lt;/span&gt;
                        {item.badge && (
                          &lt;span className="ml-auto text-xs bg-primary text-primary-foreground rounded-full px-2 py-0.5"&gt;
                            {item.badge}
                          &lt;/span&gt;
                        )}
                      &lt;/Link&gt;
                    &lt;/SidebarMenuButton&gt;
                  &lt;/SidebarMenuItem&gt;
                );
              })}
            &lt;/SidebarMenu&gt;
          &lt;/SidebarGroupContent&gt;
        &lt;/SidebarGroup&gt;
      &lt;/SidebarContent&gt;

      &lt;SidebarFooter className="border-t border-border"&gt;
        &lt;UserNav /&gt;
      &lt;/SidebarFooter&gt;
    &lt;/Sidebar&gt;
  );
}

Route-Highlight: usePathname() liefert den aktuellen Pfad, Vergleich mit item.href, bei Match isActive={true} – die Komponente setzt den Aktiv-Stil.

3.5 Mehrstufiges Menü

Für Untermenüs Collapsible um SidebarGroup bzw. Menüeinträge:

import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { ChevronDown } from "lucide-react";

// in SidebarMenu
&lt;Collapsible defaultOpen&gt;
  &lt;SidebarMenuItem&gt;
    &lt;CollapsibleTrigger asChild&gt;
      &lt;SidebarMenuButton&gt;
        &lt;Settings className="h-4 w-4" /&gt;
        &lt;span&gt;Einstellungen&lt;/span&gt;
        &lt;ChevronDown className="ml-auto h-4 w-4 transition-transform group-data-[state=open]/collapsible:rotate-180" /&gt;
      &lt;/SidebarMenuButton&gt;
    &lt;/CollapsibleTrigger&gt;
    &lt;CollapsibleContent&gt;
      &lt;SidebarMenuSub&gt;
        &lt;SidebarMenuSubItem&gt;
          &lt;SidebarMenuSubButton href="/settings/general"&gt;
            &lt;span&gt;Allgemein&lt;/span&gt;
          &lt;/SidebarMenuSubButton&gt;
        &lt;/SidebarMenuSubItem&gt;
        &lt;SidebarMenuSubItem&gt;
          &lt;SidebarMenuSubButton href="/settings/security"&gt;
            &lt;span&gt;Sicherheit&lt;/span&gt;
          &lt;/SidebarMenuSubButton&gt;
        &lt;/SidebarMenuSubItem&gt;
      &lt;/SidebarMenuSub&gt;
    &lt;/CollapsibleContent&gt;
  &lt;/SidebarMenuItem&gt;
&lt;/Collapsible&gt;

4. Erweiterte Funktionen

Basis steht – ein paar praktische Erweiterungen.

4.1 Berechtigungen (RBAC)

Menüs je Rolle: roles in der Config, beim Rendern filtern.

Config erweitern:

// lib/navigation.ts
export interface NavItem {
  title: string;
  href: string;
  icon: React.ComponentType&lt;{ className?: string }&gt;;
  roles?: string[];
}

export const navConfig: NavItem[] = [
  {
    title: "Übersicht",
    href: "/dashboard",
    icon: Home,
    // ohne roles – für alle sichtbar
  },
  {
    title: "Benutzer",
    href: "/users",
    icon: Users,
    roles: ["admin", "manager"],
  },
  {
    title: "Berechtigungen",
    href: "/permissions",
    icon: Shield,
    roles: ["admin"],
  },
];

In AppSidebar filtern:

// components/app-sidebar.tsx
import { useAuth } from "@/hooks/use-auth";

export function AppSidebar() {
  const pathname = usePathname();
  const { user } = useAuth();

  const filteredNav = navConfig.filter((item) =&gt; {
    if (!item.roles) return true;
    return item.roles.some((role) =&gt; user?.roles?.includes(role));
  });

  return (
    &lt;Sidebar&gt;
      {/* ... */}
      &lt;SidebarMenu&gt;
        {filteredNav.map((item) =&gt; {
          // ...
        })}
      &lt;/SidebarMenu&gt;
      {/* ... */}
    &lt;/Sidebar&gt;
  );
}

Normale Nutzer sehen „Berechtigungen“ nicht mehr.

Externe Links (Docs, Support) oder Gruppen mit Labels:

&lt;SidebarGroup&gt;
  &lt;SidebarGroupLabel&gt;Hauptfunktionen&lt;/SidebarGroupLabel&gt;
  &lt;SidebarGroupContent&gt;
    &lt;SidebarMenu&gt;
      {/* Hauptmenü */}
    &lt;/SidebarMenu&gt;
  &lt;/SidebarGroupContent&gt;
&lt;/SidebarGroup&gt;

&lt;SidebarGroup&gt;
  &lt;SidebarGroupLabel&gt;Hilfe &amp; Support&lt;/SidebarGroupLabel&gt;
  &lt;SidebarGroupContent&gt;
    &lt;SidebarMenu&gt;
      &lt;SidebarMenuItem&gt;
        &lt;SidebarMenuButton asChild&gt;
          &lt;a href="https://docs.example.com" target="_blank" rel="noopener"&gt;
            &lt;BookOpen className="h-4 w-4" /&gt;
            &lt;span&gt;Dokumentation&lt;/span&gt;
            &lt;ExternalLink className="ml-auto h-3 w-3" /&gt;
          &lt;/a&gt;
        &lt;/SidebarMenuButton&gt;
      &lt;/SidebarMenuItem&gt;
      &lt;SidebarMenuItem&gt;
        &lt;SidebarMenuButton asChild&gt;
          &lt;a href="mailto:[email protected]"&gt;
            &lt;HelpCircle className="h-4 w-4" /&gt;
            &lt;span&gt;Kontakt&lt;/span&gt;
          &lt;/a&gt;
        &lt;/SidebarMenuButton&gt;
      &lt;/SidebarMenuItem&gt;
    &lt;/SidebarMenu&gt;
  &lt;/SidebarGroupContent&gt;
&lt;/SidebarGroup&gt;

4.3 Suche und Shortcuts

Suchfeld oder globale Suche (Cmd+K) – shadcn/ui Command:

import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command";

&lt;SidebarGroup&gt;
  &lt;SidebarGroupContent&gt;
    &lt;Command className="rounded-lg border shadow-md"&gt;
      &lt;CommandInput placeholder="Menü durchsuchen…" /&gt;
      &lt;CommandList&gt;
        &lt;CommandEmpty&gt;Keine Treffer&lt;/CommandEmpty&gt;
        &lt;CommandGroup heading="Vorschläge"&gt;
          {navConfig.map((item) =&gt; (
            &lt;CommandItem key={item.href} onSelect={() =&gt; router.push(item.href)}&gt;
              &lt;item.icon className="mr-2 h-4 w-4" /&gt;
              {item.title}
            &lt;/CommandItem&gt;
          ))}
        &lt;/CommandGroup&gt;
      &lt;/CommandList&gt;
    &lt;/Command&gt;
  &lt;/SidebarGroupContent&gt;
&lt;/SidebarGroup&gt;

5. Performance und Best Practices

Ein paar Punkte aus der Praxis.

Server Components bevorzugen

Im App Router sind Komponenten standardmäßig Server Components. Statische Teile der Sidebar (Logo, feste Menüpunkte) können Server Components bleiben; Interaktion (Highlight, Einklappen) braucht "use client".

Vorgehen:

  • AppSidebar als "use client" (wegen usePathname)
  • statische Teile in Header/Footer als eigene Server Components
  • Nav-Config serverseitig erzeugen und an Client übergeben

Das reduziert Client-JavaScript.

Große Menüs lazy laden

Bei sehr vielen Einträgen: React.lazy oder Next.js dynamic:

import dynamic from "next/dynamic";

const AdminMenu = dynamic(() =&gt; import("./admin-menu"), {
  loading: () =&gt; &lt;SidebarMenuSkeleton /&gt;,
});

In den meisten Admins reicht das selten – Menüs sind meist überschaubar.

Barrierefreiheit

Radix UI deckt vieles ab. Trotzdem beachten:

  1. Icon + Text: nicht nur Icons – Screenreader brauchen Beschriftung
  2. Focus sichtbar: Standard-Focus-Stile nicht überschreiben
  3. Tastatur: Tab und Pfeiltasten testen

Bei eigenen Anpassungen Tastaturnavigation prüfen.


6. Häufige Fragen

F1: Sidebar-State nach Reload weg?

Wenn SidebarProvider nur im Dashboard Layout sitzt, setzt sich der State bei Navigation zurück. Provider ins Root Layout heben.

F2: Sidebar auf Mobil automatisch schließen?

Mobil wird die Sidebar zum Sheet. Nach Menüklick manuell schließen:

const { setOpenMobile } = useSidebar();

&lt;SidebarMenuButton
  onClick={() =&gt; setOpenMobile(false)}
&gt;

F3: Sidebar-Breite anpassen?

CSS-Variablen:

&lt;Sidebar
  style={{
    "--sidebar-width": "280px",
    "--sidebar-width-mobile": "100%",
  }}
&gt;

Oder SIDEBAR_WIDTH in sidebar.tsx ändern.


Fazit

shadcn/ui Sidebar mit Next.js Layout ist ein effizientes Gerüst für Admin-Oberflächen. Kurz zusammengefasst:

  1. Komponentensystem: SidebarProvider, Sidebar, SidebarContent – Rollen verstehen
  2. Layout: Route Groups trennen Layouts, SidebarProvider im Root
  3. Config: Navigation zentral, Komponente rendert daraus
  4. Highlight: usePathname() + isActive
  5. RBAC: roles in der Config, beim Rendern filtern

Diese Struktur nutze ich in mehreren Projekten – neue Seite = ein Eintrag in navConfig.

Fragen gerne in den Kommentaren. Als Nächstes: shadcn/ui DataTable in der Praxis.


Referenzen

shadcn/ui Sidebar + Next.js Layout Admin-Skeleton aufbauen

Von null ein skalierbares Admin-Layout mit Sidebar, Route-Highlight und Berechtigungssteuerung erstellen

⏱️ Estimated time: 45 min

  1. 1

    Step 1: shadcn/ui installieren und Sidebar-Komponente hinzufügen

    Projekt per CLI initialisieren und Komponente hinzufügen:

    ```bash
    npx shadcn@latest init
    npx shadcn@latest add sidebar
    ```

    Bei der Style-Konfiguration reicht die Standardeinstellung. Danach liegt sidebar.tsx unter components/ui.
  2. 2

    Step 2: Root Layout konfigurieren

    In app/layout.tsx mit SidebarProvider wrappen:

    • SidebarProvider importieren
    • {children} im body-Tag einbetten
    • lang="de" setzen

    So bleibt der Sidebar-State global persistent.
  3. 3

    Step 3: Dashboard Layout erstellen

    Unter app/(dashboard)/layout.tsx das Dashboard-Layout anlegen:

    • Route-Groups-Syntax (dashboard) nutzen
    • AppSidebar und SidebarInset importieren
    • Header und Breadcrumb oben ergänzen

    Route Groups erscheinen nicht in der URL – /dashboard mappt direkt auf den Root-Pfad.
  4. 4

    Step 4: Navigationskonfiguration definieren

    Datei lib/navigation.ts anlegen:

    • NavItem-Interface (title, href, icon, badge)
    • navConfig-Array exportieren
    • Optional roles-Feld für Berechtigungen

    Konfigurationsgesteuert: neues Menü = eine Stelle ändern.
  5. 5

    Step 5: AppSidebar-Komponente implementieren

    components/app-sidebar.tsx erstellen:

    • Mit „use client“ als Client Component markieren
    • Aktuelle Route per usePathname holen
    • navConfig durchlaufen und Menüeinträge rendern
    • Bei Route-Match isActive für Highlight setzen
  6. 6

    Step 6: Berechtigungssteuerung hinzufügen (optional)

    RBAC-Filter implementieren:

    • roles-Feld im NavItem-Interface
    • Benutzerrolle per useAuth in AppSidebar holen
    • Menüeinträge per filter einschränken

    Einträge ohne roles-Feld sind für alle sichtbar.

FAQ

Was unterscheidet shadcn/ui Sidebar von Ant Design Sidebar?
shadcn/ui folgt dem Copy-paste-Modell – der Komponentencode liegt direkt in Ihrem Projekt und ist vollständig kontrollierbar. Ant Design ist ein komplettes Designsystem: schnell startklar, aber teuer in der Anpassung. Für hohe Individualisierung: shadcn/ui. Für schnelle Entwicklung: Ant Design.
SidebarProvider im Root Layout oder im Dashboard Layout?
Empfehlung: Root Layout (app/layout.tsx). So bleibt der Einklapp-Status zwischen Seiten erhalten. Im Dashboard Layout setzt sich der State bei jedem Routenwechsel zurück.
Sidebar auf Mobilgeräten automatisch schließen?
Auf Mobilgeräten wird die Sidebar automatisch zum Sheet-Drawer. Nach Klick auf einen Menüeintrag aufrufen:

```tsx
const { setOpenMobile } = useSidebar();
&lt;SidebarMenuButton onClick={() =&gt; setOpenMobile(false)}&gt;
```

Der Drawer schließt sich danach von selbst.
Sidebar-Breite anpassen?
Zwei Wege:

1. CSS-Variablen (empfohlen):
```tsx
&lt;Sidebar style={{ "--sidebar-width": "280px" }} /&gt;
```

2. Konstante SIDEBAR_WIDTH in sidebar.tsx ändern

CSS-Variablen erlauben unterschiedliche Breiten pro Sidebar.
Mehrstufiges Menü umsetzen?
SidebarMenuItem mit Collapsible wrappen:

• CollapsibleTrigger für den Button der ersten Ebene
• CollapsibleContent mit SidebarMenuSub und Unterpunkten
• ChevronDown-Icon zeigt den Aufklapp-Zustand

Vollständiger Code in Abschnitt 3.5.
Wie barrierefrei ist shadcn/ui Sidebar?
Auf Radix UI aufgebaut – Tastaturnavigation (Tab/Pfeiltasten), ARIA-Attribute und Focus-Management sind eingebaut. Icons immer mit Text kombinieren, nicht nur Icons allein.

11 Min. Lesezeit · Veröffentlicht am: 27. März 2026 · Aktualisiert am: 13. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog