shadcn-svelte
shadcn-svelte
A framework for building UI, components, and design systems for Svelte. Components are added as source to the user's project via the CLI.
IMPORTANT: Run all CLI commands using the project's package runner:
npx shadcn-svelte@latest,pnpm dlx shadcn-svelte@latest, orbunx --bun shadcn-svelte@latest— based on the project's package manager. Examples below usenpx shadcn-svelte@latestbut substitute the correct runner for the project.
Current Project Context
Read components.json at the project root and, when you need the live file layout, list the directory given by the aliases.ui path (resolved with the same rules as the CLI).
Imports (Svelte)
Each component lives in its own folder with an index.ts barrel. Match the installation docs:
- Multi-part components (dialog, select, card, field, tabs, …):
import * as Dialog from "$lib/components/ui/dialog"thenDialog.Content,Dialog.Title,Card.Root,Card.Header, etc. — whatever the barrel exports (short names and/orRoot as …aliases). - Single-component barrels (only one meaningful component in the folder): named imports —
import { Button } from "$lib/components/ui/button"and<Button>, notimport * as Button+Button.Root. Same pattern for{ Input },{ Badge },{ Spinner },{ Checkbox },{ Separator },{ Skeleton }, etc.
import * as Dialog from "$lib/components/ui/dialog";
import { Button } from "$lib/components/ui/button";
import { Separator } from "$lib/components/ui/separator";
Use the real aliases from components.json (often $lib/components/ui/...), not hardcoded paths.
Principles
- Use existing components first. Run
npx shadcn-svelte@latest addwith no arguments to browse available components, or check Components before writing custom UI. - Compose, don't reinvent. Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
- Use built-in variants before custom styles.
variant="outline",size="sm", etc. - Use semantic colors.
bg-primary,text-muted-foreground— never raw values likebg-blue-500.
Critical Rules
These rules are always enforced. Each links to a file with Incorrect/Correct code pairs.
Styling & Tailwind → styling.md
classfor layout, not styling. Never override component colors or typography.- No
space-x-*orspace-y-*. Useflexwithgap-*. For vertical stacks,flex flex-col gap-*. - Use
size-*when width and height are equal.size-10notw-10 h-10. - Use
truncateshorthand. Notoverflow-hidden text-ellipsis whitespace-nowrap. - No manual
dark:color overrides. Use semantic tokens (bg-background,text-muted-foreground). - Use
cn()for conditional classes. Don't write manual template literal ternaries. - No manual
z-indexon overlay components. Dialog, Sheet, Popover, etc. handle their own stacking.
Forms & Inputs → forms.md
- Forms use
Field.FieldGroup+Field.Field. Never use rawdivwithspace-y-*orgrid gap-*for form layout. InputGroupusesInputGroup.Input/InputGroup.Textarea. Never rawInput/TextareainsideInputGroup.Root.- Buttons inside inputs use
InputGroup.Root+InputGroup.Addon. - Option sets (2–7 choices) use
ToggleGroup. Don't loopButtonwith manual active state. Field.FieldSet+Field.FieldLegendfor grouping related checkboxes/radios. Don't use adivwith a heading.- Field validation uses
data-invalid+aria-invalid.data-invalidonField,aria-invalidon the control. For disabled:data-disabledonField,disabledon the control.
Component Structure → composition.md
- Items always inside their Group.
Select.Item→Select.Group.DropdownMenu.Item→DropdownMenu.Group.Command.Item→Command.Group. - Custom triggers. Wrap controls in
Dialog.Trigger/AlertDialog.Trigger, or control open state withbind:openon the root — see component docs. - Dialog, Sheet, and Drawer always need a Title.
Dialog.Title,Sheet.Title,Drawer.Titlerequired for accessibility. Useclass="sr-only"if visually hidden. - Use full Card composition.
Card.Header/Card.Title/Card.Description/Card.Content/Card.Footer. Don't dump everything inCard.Content. - Button has no
isPending/isLoading. Compose withSpinnerinsideButton+disabled; usedata-icon="inline-start"/inline-endonSpinnerfor correct spacing (import { Button },import { Spinner }). Tabs.Triggermust be insideTabs.List. Never render triggers directly inTabs.Avataralways needsAvatar.Fallback. For when the image fails to load.
Use Components, Not Custom Markup → composition.md
- Use existing components before custom markup. Check if a component exists before writing a styled
div. - Callouts use
Alert. Don't build custom styled divs. - Empty states use
Empty. Don't build custom empty state markup. - Toast via
svelte-sonner. Usetoast()fromsvelte-sonnerwith the Sonner component from your UI folder. - Use
Separatorinstead of<hr>or adivwith border-only classes. - Use
Skeletonfor loading placeholders. No customanimate-pulsedivs. - Use
Badgeinstead of custom styled spans.
Icons → icons.md
- Icons in
<Button>usedata-icon.data-icon="inline-start"ordata-icon="inline-end"on the icon. - No sizing classes on icons inside components. Components handle icon sizing via CSS. No
size-4orw-4 h-4. - Pass icons as components. Import from the configured
iconLibrary(e.g.@lucide/svelte), not string keys.
CLI
- Presets — copy the encoded string from the design-system builder on shadcn-svelte.com and pass it to
npx shadcn-svelte@latest init --preset <code>.
Key Patterns
These are the most common patterns that differentiate correct shadcn-svelte code. For edge cases, see the linked rule files above.
<script lang="ts">
import * as Field from "$lib/components/ui/field";
import { Input } from "$lib/components/ui/input";
import { Button } from "$lib/components/ui/button";
import SearchIcon from "@lucide/svelte/icons/search";
import { Badge } from "$lib/components/ui/badge";
import * as Avatar from "$lib/components/ui/avatar";
</script>
<!-- Form layout: Field.FieldGroup + Field.Field, not div + Label. -->
<Field.FieldGroup>
<Field.Field>
<Field.FieldLabel for="email">Email</Field.FieldLabel>
<Input id="email" />
</Field.Field>
</Field.FieldGroup>
<!-- Validation: data-invalid on Field, aria-invalid on the control. -->
<Field.Field data-invalid>
<Field.FieldLabel for="email">Email</Field.FieldLabel>
<Input id="email" aria-invalid />
<Field.FieldDescription>Invalid email.</Field.FieldDescription>
</Field.Field>
<!-- Icons in buttons: data-icon, no sizing classes. -->
<Button>
<SearchIcon data-icon="inline-start" />
Search
</Button>
<!-- Spacing: gap-*, not space-y-*. -->
<div class="flex flex-col gap-4"></div>
<!-- Equal dimensions: size-*, not w-* h-*. -->
<Avatar.Root class="size-10">
<Avatar.Image src="/u.png" alt="User" />
<Avatar.Fallback>U</Avatar.Fallback>
</Avatar.Root>
<!-- Status colors: Badge variants or semantic tokens, not raw colors. -->
<Badge variant="secondary">+20.1%</Badge>
Component Selection
| Need | Use |
|---|---|
| Button/action | Button with appropriate variant (import { Button }) |
| Form inputs | Input, Select, Combobox, Switch, Checkbox, RadioGroup, Textarea, InputOTP, Slider |
| Toggle between 2–5 options | ToggleGroup.Root + ToggleGroup.Item |
| Data display | Table, Card, Badge, Avatar |
| Navigation | Sidebar, NavigationMenu, Breadcrumb, Tabs, Pagination |
| Overlays | Dialog (modal), Sheet (side panel), Drawer (bottom sheet), AlertDialog (confirmation) |
| Feedback | svelte-sonner (toast), Alert, Progress, Skeleton, Spinner |
| Command palette | Command inside Dialog |
| Charts | Chart (LayerChart) |
| Layout | Card, Separator, Resizable, ScrollArea, Accordion, Collapsible |
| Empty states | Empty |
| Menus | DropdownMenu, ContextMenu, Menubar |
| Tooltips/info | Tooltip, HoverCard, Popover |
Key Fields
Use components.json and the filesystem — not a separate info command:
aliases→ use the actual alias prefix from config (e.g.$lib/), never hardcode unrelated projects.tailwind.css→ the global CSS file where theme variables live. Edit this file for theme tweaks; don't add a second globals file unless the user already uses one.style→ visual treatment (e.g.nova,vega, …) and registry style path.iconLibrary→ determines icon packages (@lucide/svelte,@tabler/icons-svelte, etc.). Never assume@lucide/svelte.registry→ where the CLI fetches components; default official registry atshadcn-svelte.com.resolvedPaths(conceptual) → the CLI resolvesaliasesto absolute paths; listaliases.uion disk to see installed components.
See cli.md for commands and flags.
Component Docs, Examples, and Usage
Open https://shadcn-svelte.com/docs/components/<name>.md for docs and examples. When creating, fixing, debugging, or using a component, read the official page first so you follow the documented APIs.
Workflow
- Get project context — read
components.jsonand list the UI components directory when needed. - Check installed components first — before running
add, list files under the resolveduipath. Don't import components that haven't been added, and don't re-add ones already present unless updating. - Discover components —
npx shadcn-svelte@latest addwith no arguments (interactive list), or the docs site. - Install or update —
npx shadcn-svelte@latest add <name>or a registry URL. To refresh existing files from the registry, usenpx shadcn-svelte@latest update(see cli.md). - Fix imports in third-party / URL-added items — After adding from a custom registry URL, check for hardcoded paths that don't match the project's
aliases. Rewrite imports to use the project'sui/libaliases fromcomponents.json. - Review added components — After adding, read the added files and verify composition (groups, titles, validation attrs). Align icon imports with
iconLibrary. - Remote registry items — Adding by URL is explicit; if the user wants a component from an unknown source, confirm the registry URL or item before running
add.
Updating Components
Use the update command to pull the latest registry versions of components already in the project. Review changes with git diff after update.
- Commit or stash local work.
- Run
npx shadcn-svelte@latest update [component]or--all. - Resolve merge conflicts if you had customized files.
- Never use
--overwriteonaddwithout the user's explicit approval when it would destroy intentional edits.
Quick Reference
# Initialize shadcn-svelte in your project.
npx shadcn-svelte@latest init
# Initialize with a preset string from the docs site builder.
npx shadcn-svelte@latest init --preset <code>
# Add components (interactive when run with no names).
npx shadcn-svelte@latest add
npx shadcn-svelte@latest add button card dialog
npx shadcn-svelte@latest add --all
# Update components already installed.
npx shadcn-svelte@latest update button
npx shadcn-svelte@latest update --all --yes
# Build a custom registry (registry authors).
npx shadcn-svelte@latest registry build
Registry: default https://shadcn-svelte.com/registry — override in components.json if needed.
Docs: shadcn-svelte.com
Detailed References
- rules/forms.md — Field.FieldGroup, Field.Field, InputGroup, ToggleGroup, Field.FieldSet, validation states
- rules/composition.md — Groups, overlays, Card, Tabs, Avatar, Alert, Empty, Toast, Separator, Skeleton, Badge, Button loading
- rules/icons.md — data-icon, icon sizing, passing icon components
- rules/styling.md — Semantic colors, variants, class, spacing, size, truncate, dark mode, cn(), z-index
- cli.md — Commands, flags, registry
- customization.md — Theming, CSS variables, extending components