SKILL.md
shadcn-vue
A framework for building ui, components and design systems. Components are added as source code to the user's project via the CLI.
IMPORTANT: Run all CLI commands using the project's package runner: npx shadcn-vue@latest, pnpm dlx shadcn-vue@latest, or bunx --bun shadcn-vue@latest — based on the project's packageManager. Examples below use npx shadcn-vue@latest but substitute the correct runner for the project.
Current Project Context
!`npx shadcn-vue@latest info --json`
The JSON above contains the project config and installed components. Use npx shadcn-vue@latest docs <component> to get documentation and example URLs for any component.
Principles
- Use existing components first. Use
npx shadcn-vue@latest searchto check registries before writing custom UI. Check community registries too.
- 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
FieldGroup+Field.** Never use rawdivwithspace-y-*orgrid gap-*for form layout.
- **
InputGroupusesInputGroupInput/InputGroupTextarea.** Never rawInput/TextareainsideInputGroup.
- **Buttons inside inputs use
InputGroup+InputGroupAddon.**
- **Option sets (2–7 choices) use
ToggleGroup.** Don't loopButtonwith manual active state.
- **
FieldSet+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.
SelectItem→SelectGroup.DropdownMenuItem→DropdownMenuGroup.CommandItem→CommandGroup.
- Dialog, Sheet, and Drawer always need a Title.
DialogTitle,SheetTitle,DrawerTitlerequired for accessibility. Useclass="sr-only"if visually hidden.
- Use full Card composition.
CardHeader/CardTitle/CardDescription/CardContent/CardFooter. Don't dump everything inCardContent.
- **Button has no
isPending/isLoading.** Compose withSpinner+data-icon+disabled.
- **
TabsTriggermust be insideTabsList.** Never render triggers directly inTabs.
- **
Avataralways needsAvatarFallback.** 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
vue-sonner.** Usetoast()fromvue-sonner.
- **Use
Separator** instead of<hr>or<div class="border-t">.
- **Use
Skeleton** for loading placeholders. No customanimate-pulsedivs.
- **Use
Badge** instead of custom styled spans.
Icons → icons.md
- **Icons in
Buttonusedata-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 objects, not string keys.
:icon="CheckIcon", not a string lookup.
CLI
- Apply preset codes directly with the CLI. Use
npx shadcn-vue@latest apply <code>for existing projects, ornpx shadcn-vue@latest init --preset <code>when initializing.
Key Patterns
These are the most common patterns that differentiate correct shadcn-vue code. For edge cases, see the linked rule files above.
<!-- Form layout: FieldGroup + Field, not div + Label. -->
<FieldGroup>
<Field>
<FieldLabel for="email">Email</FieldLabel>
<Input id="email" />
</Field>
</FieldGroup>
<!-- Validation: data-invalid on Field, aria-invalid on the control. -->
<Field data-invalid>
<FieldLabel>Email</FieldLabel>
<Input aria-invalid />
<FieldDescription>Invalid email.</FieldDescription>
</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"> <!-- correct -->
<div class="space-y-4"> <!-- wrong -->
<!-- Equal dimensions: size-*, not w-* h-*. -->
<Avatar class="size-10"> <!-- correct -->
<Avatar class="w-10 h-10"> <!-- wrong -->
<!-- Status colors: Badge variants or semantic tokens, not raw colors. -->
<Badge variant="secondary">+20.1%</Badge> <!-- correct -->
<span class="text-emerald-600">+20.1%</span> <!-- wrong -->
Component Selection
Need
Use
Button/action
Button with appropriate variant
Form inputs
Input, Select, Combobox, Switch, Checkbox, RadioGroup, Textarea, InputOTP, Slider
Toggle between 2–7 options
ToggleGroup + ToggleGroupItem
Data display
Table, Card, Badge, Avatar
Navigation
Sidebar, NavigationMenu, Breadcrumb, Tabs, Pagination
Overlays
Dialog (modal), Sheet (side panel), Drawer (bottom sheet), AlertDialog (confirmation)
Feedback
vue-sonner (toast), Alert, Progress, Skeleton, Spinner
Command palette
Command inside Dialog
Charts
Chart (wraps Unovis)
Layout
Card, Separator, Resizable, ScrollArea, Accordion, Collapsible
Empty states
Empty
Menus
DropdownMenu, ContextMenu, Menubar
Tooltips/info
Tooltip, HoverCard, Popover
Key Fields
The injected project context contains these key fields:
- **
aliases** → use the actual alias prefix for imports (e.g.@/,~/), never hardcode.
- **
tailwindVersion** →"v4"uses@theme inlineblocks;"v3"usestailwind.config.js.
- **
tailwindCssFile** → the global CSS file where custom CSS variables are defined. Always edit this file, never create a new one.
- **
style** → component visual treatment (e.g.nova,vega).
- **
base** → primitive library (reka). Affects component APIs and available props.
- **
iconLibrary** → determines icon imports. Uselucide-vue-nextforlucide,@tabler/icons-vuefortabler, etc. Never assumelucide-vue-next.
- **
resolvedPaths** → exact file-system destinations for components, utils, hooks, etc.
- **
framework** → routing and file conventions (e.g. Nuxt vs Vite SPA).
- **
packageManager** → use this for any non-shadcn-vue dependency installs (e.g.pnpm add date-fnsvsnpm install date-fns).
See cli.md — info command for the full field reference.
Component Docs, Examples, and Usage
Run npx shadcn-vue@latest docs <component> to get the URLs for a component's documentation, examples, and API reference. Fetch these URLs to get the actual content.
npx shadcn-vue@latest docs button dialog select
**When creating, fixing, debugging, or using a component, always run npx shadcn-vue@latest docs and fetch the URLs first.** This ensures you're working with the correct API and usage patterns rather than guessing.
Workflow
- Get project context — already injected above. Run
npx shadcn-vue@latest infoagain if you need to refresh.
- Check installed components first — before running
add, always check thecomponentslist from project context or list theresolvedPaths.uidirectory. Don't import components that haven't been added, and don't re-add ones already installed.
- Find components —
npx shadcn-vue@latest search.
- Get docs and examples — run
npx shadcn-vue@latest docs <component>to get URLs, then fetch them. Usenpx shadcn-vue@latest viewto browse registry items you haven't installed. To preview changes to installed components, usenpx shadcn-vue@latest add --diff.
- Install or update —
npx shadcn-vue@latest add. When updating existing components, use--dry-runand--diffto preview changes first (see [Updating Components](#updating-components) below).
- Fix imports in third-party components — After adding components from community registries, check the added non-UI files for hardcoded import paths like
@/components/ui/.... These won't match the project's actual aliases. Usenpx shadcn-vue@latest infoto get the correctuialias (e.g.@workspace/ui/components) and rewrite the imports accordingly. The CLI rewrites imports for its own UI files, but third-party registry components may use default paths that don't match the project.
- Review added components — After adding a component or block from any registry, always read the added files and verify they are correct. Check for missing sub-components (e.g.
SelectItemwithoutSelectGroup), missing imports, incorrect composition, or violations of the [Critical Rules](#critical-rules). Also replace any icon imports with the project'siconLibraryfrom the project context (e.g. if the registry item useslucide-vue-nextbut the project useshugeicons, swap the imports and icon names accordingly). Fix all issues before moving on.
- Registry must be explicit — When the user asks to add a block or component, do not guess the registry. If no registry is specified (e.g. user says "add a login block" without specifying
@shadcn, etc.), ask which registry to use. Never default to a registry on behalf of the user.
- Switching presets — Ask the user first: overwrite, merge, or skip?
- Overwrite:
npx shadcn-vue@latest apply <code>. Overwrites detected components, fonts, and CSS variables.
- Merge:
npx shadcn-vue@latest init --preset <code> --force --no-reinstall, then runnpx shadcn-vue@latest infoto list installed components, then for each installed component use--dry-runand--diffto [smart merge](#updating-components) it individually.
- Skip:
npx shadcn-vue@latest init --preset <code> --force --no-reinstall. Only updates config and CSS, leaves components as-is.
- Important: Always run preset commands inside the user's project directory.
applyonly works in an existing project with acomponents.jsonfile. The CLI automatically preserves the current base (reka) fromcomponents.json. If you must use a scratch/temp directory (e.g. for--dry-runcomparisons), pass--base <current-base>explicitly — preset codes do not encode the base.
Updating Components
When the user asks to update a component from upstream while keeping their local changes, use --dry-run and --diff to intelligently merge. NEVER fetch raw files from GitHub manually — always use the CLI.
- Run
npx shadcn-vue@latest add <component> --dry-runto see all files that would be affected.
- For each file, run
npx shadcn-vue@latest add <component> --diff <file>to see what changed upstream vs local.
- Decide per file based on the diff:
- No local changes → safe to overwrite.
- Has local changes → read the local file, analyze the diff, and apply upstream updates while preserving local modifications.
- User says "just update everything" → use
--overwrite, but confirm first.
- **Never use
--overwritewithout the user's explicit approval.**
Quick Reference
# Create a new project.
npx shadcn-vue@latest init --name my-app --preset nova
npx shadcn-vue@latest init --name my-app --preset a2r6bw --template vite
# Initialize existing project.
npx shadcn-vue@latest init --preset nova
npx shadcn-vue@latest init --defaults # shortcut: --template=nuxt --preset=nova (base style implied)
# Apply a preset to an existing project.
npx shadcn-vue@latest apply a2r6bw
# Add components.
npx shadcn-vue@latest add button card dialog
npx shadcn-vue@latest add --all
# Search registries.
npx shadcn-vue@latest search @shadcn -q "sidebar"
# Get component docs and example URLs.
npx shadcn-vue@latest docs button dialog select
# View registry item details (for items not yet installed).
npx shadcn-vue@latest view @shadcn/button
Named presets: nova, vega, maia, lyra, mira, luma
Templates: nuxt, vite, astro and laravel
Preset codes: Version-prefixed base62 strings (e.g. a2r6bw), from shadcn-vue.com.
Detailed References
- rules/forms.md — FieldGroup, Field, InputGroup, ToggleGroup, 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 icons as objects
- rules/styling.md — Semantic colors, variants, class, spacing, size, truncate, dark mode, cn(), z-index
- cli.md — Commands, flags, presets, templates
- customization.md — Theming, CSS variables, extending components