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: This repo uses npm. Run shadcn-svelte CLI commands from src/Exceptionless.Web/ClientApp with npx shadcn-svelte@latest ....
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).
Only import components that exist under the resolved aliases.ui directory. If a registry component is useful but not installed, add it intentionally with the CLI before importing it.
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 "$comp/ui/dialog" then Dialog.Content, Dialog.Title, Card.Root, Card.Header, etc. — whatever the barrel exports (short names and/or Root as … aliases).
- Single-component barrels (only one meaningful component in the folder): named imports —
import { Button } from "$comp/ui/button" and <Button>, not import * as Button + Button.Root. Same pattern for { Input }, { Badge }, { Spinner }, { Checkbox }, { Separator }, { Skeleton }, etc.
import * as Dialog from "$comp/ui/dialog";
import { Button } from "$comp/ui/button";
import { Separator } from "$comp/ui/separator";
Use the real aliases from components.json; this repo uses $comp/ui/..., not $lib/components/ui/....
Principles
- Use existing components first. Run
npx shadcn-svelte@latest add with 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 like bg-blue-500.
Critical Rules
These rules are always enforced. Each links to a file with Incorrect/Correct code pairs.
Styling & Tailwind → styling.md
class for layout, not styling. Never override component colors or typography.
- No
space-x-* or space-y-*. Use flex with gap-*. For vertical stacks, flex flex-col gap-*.
- Use
size-* when width and height are equal. size-10 not w-10 h-10.
- Use
truncate shorthand. Not overflow-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-index on overlay components. Dialog, Sheet, Popover, etc. handle their own stacking.
Forms & Inputs → forms.md
- Forms use
Field.FieldGroup + Field.Field. Never use raw div with space-y-* or grid gap-* for form layout.
InputGroup uses InputGroup.Input/InputGroup.Textarea. Never raw Input/Textarea inside InputGroup.Root.
- Buttons inside inputs use
InputGroup.Root + InputGroup.Addon.
- Option sets use installed form controls. Prefer
RadioGroup, Select, or an intentionally added ToggleGroup; don't loop Button with manual active state.
Field.FieldSet + Field.FieldLegend for grouping related checkboxes/radios. Don't use a div with a heading.
- Field validation uses
data-invalid + aria-invalid. data-invalid on Field, aria-invalid on the control. For disabled: data-disabled on Field, disabled on the control.
- 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 with bind:open on the root — see component docs.
- Dialog and Sheet always need a Title.
Dialog.Title and Sheet.Title are required for accessibility. Use class="sr-only" if visually hidden.
- Use full Card composition.
Card.Header/Card.Title/Card.Description/Card.Content/Card.Footer. Don't dump everything in Card.Content.
- Button has no
isPending/isLoading. Compose with Spinner inside Button + disabled; use data-icon="inline-start" / inline-end on Spinner for correct spacing (import { Button }, import { Spinner }).
Tabs.Trigger must be inside Tabs.List. Never render triggers directly in Tabs.
Avatar always needs Avatar.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 installed primitives. Use
Card, Alert, Button, Skeleton, and Badge unless the Empty component has been intentionally added.
- Toast via
svelte-sonner. Use toast() from svelte-sonner with the Sonner component from your UI folder.
- Use
Separator instead of <hr> or a div with border-only classes.
- Use
Skeleton for loading placeholders. No custom animate-pulse divs.
- Use
Badge instead of custom styled spans.
- Icons in
<Button> use data-icon. data-icon="inline-start" or data-icon="inline-end" on the icon.
- No sizing classes on icons inside components. Components handle icon sizing via CSS. No
size-4 or w-4 h-4.
- Pass icons as components. This repo currently uses
@lucide/svelte icons; if components.json later adds iconLibrary, follow that field.
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 "$comp/ui/field";
import { Input } from "$comp/ui/input";
import { Button } from "$comp/ui/button";
import SearchIcon from "@lucide/svelte/icons/search";
import { Badge } from "$comp/ui/badge";
import * as Avatar from "$comp/ui/avatar";
</script>
<!-- Form layout: Field.FieldGroup + Field.Field, not div + Label. -->
<Field.FieldGroup>
<Field.Field>
<Field.Label for="email">Email</Field.Label>
<Input id="email" />
</Field.Field>
</Field.FieldGroup>
<!-- Validation: data-invalid on Field, aria-invalid on the control. -->
<Field.Field data-invalid>
<Field.Label for="email">Email</Field.Label>
<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, Switch, Checkbox, RadioGroup, Textarea, Field, InputGroup |
| Toggle between 2–5 options |
RadioGroup, Select, or intentionally add ToggleGroup before importing it |
| Data display |
Table, Card, Badge, Avatar |
| Navigation |
Sidebar, Breadcrumb, Tabs, Pagination |
| Overlays |
Dialog (modal), Sheet (side panel), AlertDialog (confirmation), Popover |
| Feedback |
svelte-sonner (toast), Alert, Skeleton, Spinner |
| Command palette |
Command inside Dialog |
| Charts |
Chart (LayerChart) |
| Layout |
Card, Separator, Collapsible |
| Empty states |
Compose installed primitives, or intentionally add Empty before importing it |
| Menus |
DropdownMenu, ContextMenu |
| Tooltips/info |
Tooltip, 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 when present (e.g. nova, vega, …) and registry style path.
iconLibrary → determines icon packages when present. This repo currently does not set it, so infer from existing imports/package dependencies; current app code uses @lucide/svelte.
registry → where the CLI fetches components; default official registry at shadcn-svelte.com.
resolvedPaths (conceptual) → the CLI resolves aliases to absolute paths; list aliases.ui on 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 when adding a new component, updating one from the registry, or when the local component API is unclear.
Workflow
- Get project context — read
components.json and list the UI components directory when needed.
- Check installed components first — before running
add, list files under the resolved ui path. 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 add with 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, use npx 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's ui / lib aliases from components.json.
- Review added components — After adding, read the added files and verify composition (groups, titles, validation attrs). Align icon imports with
components.json when it specifies an icon library; otherwise follow this repo's existing @lucide/svelte imports.
- 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 current work first, or get explicit user approval before stashing.
- Run
npx shadcn-svelte@latest update [component] or --all.
- Resolve merge conflicts if you had customized files.
- Never use
--overwrite on add without 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, installed option controls, Field.FieldSet, validation states
- rules/composition.md — Groups, overlays, Card, Tabs, Avatar, Alert, empty states, 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
1---2name: shadcn-svelte3description: Manages shadcn-svelte components already used by the Svelte ClientApp. Use when adding/updating shadcn-svelte components, fixing shadcn component composition, consulting shadcn docs, or handling registry/CLI work. For ordinary Exceptionless frontend placement, Svelte, TypeScript, accessibility, or testing guidance, use frontend-architecture first.4---56# shadcn-svelte78A framework for building UI, components, and design systems for Svelte. Components are added as source to the user's project via the CLI.910> **IMPORTANT:** This repo uses npm. Run shadcn-svelte CLI commands from `src/Exceptionless.Web/ClientApp` with `npx shadcn-svelte@latest ...`.1112## Current Project Context1314Read `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).1516Only import components that exist under the resolved `aliases.ui` directory. If a registry component is useful but not installed, add it intentionally with the CLI before importing it.1718## Imports (Svelte)1920Each component lives in its own folder with an `index.ts` barrel. Match the [installation docs](https://shadcn-svelte.com/docs/installation):2122- **Multi-part components** (dialog, select, card, field, tabs, …): `import * as Dialog from "$comp/ui/dialog"` then `Dialog.Content`, `Dialog.Title`, `Card.Root`, `Card.Header`, etc. — whatever the barrel exports (short names and/or `Root as …` aliases).23- **Single-component barrels** (only one meaningful component in the folder): **named imports** — `import { Button } from "$comp/ui/button"` and `<Button>`, not `import * as Button` + `Button.Root`. Same pattern for `{ Input }`, `{ Badge }`, `{ Spinner }`, `{ Checkbox }`, `{ Separator }`, `{ Skeleton }`, etc.2425```ts26import * as Dialog from "$comp/ui/dialog";27import { Button } from "$comp/ui/button";28import { Separator } from "$comp/ui/separator";29```3031Use the real aliases from `components.json`; this repo uses `$comp/ui/...`, not `$lib/components/ui/...`.3233## Principles34351. **Use existing components first.** Run `npx shadcn-svelte@latest add` with no arguments to browse available components, or check [Components](https://shadcn-svelte.com/docs/components) before writing custom UI.362. **Compose, don't reinvent.** Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.373. **Use built-in variants before custom styles.** `variant="outline"`, `size="sm"`, etc.384. **Use semantic colors.** `bg-primary`, `text-muted-foreground` — never raw values like `bg-blue-500`.3940## Critical Rules4142These rules are **always enforced**. Each links to a file with Incorrect/Correct code pairs.4344### Styling & Tailwind → [styling.md](./rules/styling.md)4546- **`class` for layout, not styling.** Never override component colors or typography.47- **No `space-x-*` or `space-y-*`.** Use `flex` with `gap-*`. For vertical stacks, `flex flex-col gap-*`.48- **Use `size-*` when width and height are equal.** `size-10` not `w-10 h-10`.49- **Use `truncate` shorthand.** Not `overflow-hidden text-ellipsis whitespace-nowrap`.50- **No manual `dark:` color overrides.** Use semantic tokens (`bg-background`, `text-muted-foreground`).51- **Use `cn()` for conditional classes.** Don't write manual template literal ternaries.52- **No manual `z-index` on overlay components.** Dialog, Sheet, Popover, etc. handle their own stacking.5354### Forms & Inputs → [forms.md](./rules/forms.md)5556- **Forms use `Field.FieldGroup` + `Field.Field`.** Never use raw `div` with `space-y-*` or `grid gap-*` for form layout.57- **`InputGroup` uses `InputGroup.Input`/`InputGroup.Textarea`.** Never raw `Input`/`Textarea` inside `InputGroup.Root`.58- **Buttons inside inputs use `InputGroup.Root` + `InputGroup.Addon`.**59- **Option sets use installed form controls.** Prefer `RadioGroup`, `Select`, or an intentionally added `ToggleGroup`; don't loop `Button` with manual active state.60- **`Field.FieldSet` + `Field.FieldLegend` for grouping related checkboxes/radios.** Don't use a `div` with a heading.61- **Field validation uses `data-invalid` + `aria-invalid`.** `data-invalid` on `Field`, `aria-invalid` on the control. For disabled: `data-disabled` on `Field`, `disabled` on the control.6263### Component Structure → [composition.md](./rules/composition.md)6465- **Items always inside their Group.** `Select.Item` → `Select.Group`. `DropdownMenu.Item` → `DropdownMenu.Group`. `Command.Item` → `Command.Group`.66- **Custom triggers.** Wrap controls in `Dialog.Trigger` / `AlertDialog.Trigger`, or control open state with `bind:open` on the root — see component docs.67- **Dialog and Sheet always need a Title.** `Dialog.Title` and `Sheet.Title` are required for accessibility. Use `class="sr-only"` if visually hidden.68- **Use full Card composition.** `Card.Header`/`Card.Title`/`Card.Description`/`Card.Content`/`Card.Footer`. Don't dump everything in `Card.Content`.69- **Button has no `isPending`/`isLoading`.** Compose with `Spinner` inside `Button` + `disabled`; use `data-icon="inline-start"` / `inline-end` on `Spinner` for correct spacing (`import { Button }`, `import { Spinner }`).70- **`Tabs.Trigger` must be inside `Tabs.List`.** Never render triggers directly in `Tabs`.71- **`Avatar` always needs `Avatar.Fallback`.** For when the image fails to load.7273### Use Components, Not Custom Markup → [composition.md](./rules/composition.md)7475- **Use existing components before custom markup.** Check if a component exists before writing a styled `div`.76- **Callouts use `Alert`.** Don't build custom styled divs.77- **Empty states use installed primitives.** Use `Card`, `Alert`, `Button`, `Skeleton`, and `Badge` unless the `Empty` component has been intentionally added.78- **Toast via `svelte-sonner`.** Use `toast()` from `svelte-sonner` with the Sonner component from your UI folder.79- **Use `Separator`** instead of `<hr>` or a `div` with border-only classes.80- **Use `Skeleton`** for loading placeholders. No custom `animate-pulse` divs.81- **Use `Badge`** instead of custom styled spans.8283### Icons → [icons.md](./rules/icons.md)8485- **Icons in `<Button>` use `data-icon`.** `data-icon="inline-start"` or `data-icon="inline-end"` on the icon.86- **No sizing classes on icons inside components.** Components handle icon sizing via CSS. No `size-4` or `w-4 h-4`.87- **Pass icons as components.** This repo currently uses `@lucide/svelte` icons; if `components.json` later adds `iconLibrary`, follow that field.8889### CLI9091- **Presets** — copy the encoded string from the design-system builder on [shadcn-svelte.com](https://shadcn-svelte.com) and pass it to `npx shadcn-svelte@latest init --preset <code>`.9293## Key Patterns9495These are the most common patterns that differentiate correct shadcn-svelte code. For edge cases, see the linked rule files above.9697```svelte98<script lang="ts">99 import * as Field from "$comp/ui/field";100 import { Input } from "$comp/ui/input";101 import { Button } from "$comp/ui/button";102 import SearchIcon from "@lucide/svelte/icons/search";103 import { Badge } from "$comp/ui/badge";104 import * as Avatar from "$comp/ui/avatar";105</script>106107<!-- Form layout: Field.FieldGroup + Field.Field, not div + Label. -->108<Field.FieldGroup>109 <Field.Field>110 <Field.Label for="email">Email</Field.Label>111 <Input id="email" />112 </Field.Field>113</Field.FieldGroup>114115<!-- Validation: data-invalid on Field, aria-invalid on the control. -->116<Field.Field data-invalid>117 <Field.Label for="email">Email</Field.Label>118 <Input id="email" aria-invalid />119 <Field.FieldDescription>Invalid email.</Field.FieldDescription>120</Field.Field>121122<!-- Icons in buttons: data-icon, no sizing classes. -->123<Button>124 <SearchIcon data-icon="inline-start" />125 Search126</Button>127128<!-- Spacing: gap-*, not space-y-*. -->129<div class="flex flex-col gap-4"></div>130131<!-- Equal dimensions: size-*, not w-* h-*. -->132<Avatar.Root class="size-10">133 <Avatar.Image src="/u.png" alt="User" />134 <Avatar.Fallback>U</Avatar.Fallback>135</Avatar.Root>136137<!-- Status colors: Badge variants or semantic tokens, not raw colors. -->138<Badge variant="secondary">+20.1%</Badge>139```140141## Component Selection142143| Need | Use |144| -------------------------- | --------------------------------------------------------------------------------------------- |145| Button/action | `Button` with appropriate variant (`import { Button }`) |146| Form inputs | `Input`, `Select`, `Switch`, `Checkbox`, `RadioGroup`, `Textarea`, `Field`, `InputGroup` |147| Toggle between 2–5 options | `RadioGroup`, `Select`, or intentionally add `ToggleGroup` before importing it |148| Data display | `Table`, `Card`, `Badge`, `Avatar` |149| Navigation | `Sidebar`, `Breadcrumb`, `Tabs`, `Pagination` |150| Overlays | `Dialog` (modal), `Sheet` (side panel), `AlertDialog` (confirmation), `Popover` |151| Feedback | `svelte-sonner` (toast), `Alert`, `Skeleton`, `Spinner` |152| Command palette | `Command` inside `Dialog` |153| Charts | `Chart` (LayerChart) |154| Layout | `Card`, `Separator`, `Collapsible` |155| Empty states | Compose installed primitives, or intentionally add `Empty` before importing it |156| Menus | `DropdownMenu`, `ContextMenu` |157| Tooltips/info | `Tooltip`, `Popover` |158159## Key Fields160161Use `components.json` and the filesystem — not a separate `info` command:162163- **`aliases`** → use the actual alias prefix from config (e.g. `$lib/`), never hardcode unrelated projects.164- **`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.165- **`style`** → visual treatment when present (e.g. `nova`, `vega`, …) and registry style path.166- **`iconLibrary`** → determines icon packages when present. This repo currently does not set it, so infer from existing imports/package dependencies; current app code uses `@lucide/svelte`.167- **`registry`** → where the CLI fetches components; default official registry at `shadcn-svelte.com`.168- **`resolvedPaths`** (conceptual) → the CLI resolves `aliases` to absolute paths; list `aliases.ui` on disk to see installed components.169170See [cli.md](./cli.md) for commands and flags.171172## Component Docs, Examples, and Usage173174Open `https://shadcn-svelte.com/docs/components/<name>.md` when adding a new component, updating one from the registry, or when the local component API is unclear.175176## Workflow1771781. **Get project context** — read `components.json` and list the UI components directory when needed.1792. **Check installed components first** — before running `add`, list files under the resolved `ui` path. Don't import components that haven't been added, and don't re-add ones already present unless updating.1803. **Discover components** — `npx shadcn-svelte@latest add` with no arguments (interactive list), or the docs site.1814. **Install or update** — `npx shadcn-svelte@latest add <name>` or a registry **URL**. To refresh existing files from the registry, use `npx shadcn-svelte@latest update` (see [cli.md](./cli.md)).1825. **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's `ui` / `lib` aliases from `components.json`.1836. **Review added components** — After adding, read the added files and verify composition (groups, titles, validation attrs). Align icon imports with `components.json` when it specifies an icon library; otherwise follow this repo's existing `@lucide/svelte` imports.1847. **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`.185186## Updating Components187188Use the **`update`** command to pull the latest registry versions of components already in the project. Review changes with `git diff` after `update`.1891901. Commit current work first, or get explicit user approval before stashing.1912. Run `npx shadcn-svelte@latest update [component]` or `--all`.1923. Resolve merge conflicts if you had customized files.1934. **Never use `--overwrite` on `add` without the user's explicit approval** when it would destroy intentional edits.194195## Quick Reference196197```bash198# Initialize shadcn-svelte in your project.199npx shadcn-svelte@latest init200201# Initialize with a preset string from the docs site builder.202npx shadcn-svelte@latest init --preset <code>203204# Add components (interactive when run with no names).205npx shadcn-svelte@latest add206npx shadcn-svelte@latest add button card dialog207npx shadcn-svelte@latest add --all208209# Update components already installed.210npx shadcn-svelte@latest update button211npx shadcn-svelte@latest update --all --yes212213# Build a custom registry (registry authors).214npx shadcn-svelte@latest registry build215```216217**Registry:** default `https://shadcn-svelte.com/registry` — override in `components.json` if needed. 218**Docs:** [shadcn-svelte.com](https://shadcn-svelte.com)219220## Detailed References221222- [rules/forms.md](./rules/forms.md) — Field.FieldGroup, Field.Field, InputGroup, installed option controls, Field.FieldSet, validation states223- [rules/composition.md](./rules/composition.md) — Groups, overlays, Card, Tabs, Avatar, Alert, empty states, Toast, Separator, Skeleton, Badge, Button loading224- [rules/icons.md](./rules/icons.md) — data-icon, icon sizing, passing icon components225- [rules/styling.md](./rules/styling.md) — Semantic colors, variants, class, spacing, size, truncate, dark mode, cn(), z-index226- [cli.md](./cli.md) — Commands, flags, registry227- [customization.md](./customization.md) — Theming, CSS variables, extending components