shadcn-svelte-skill

shadcn-svelte with Tailwind v4.1 + Vite

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "shadcn-svelte-skill" with this command: npx skills add linehaul-ai/linehaulai-claude-marketplace/linehaul-ai-linehaulai-claude-marketplace-shadcn-svelte-skill

shadcn-svelte with Tailwind v4.1 + Vite

A comprehensive guide for working with shadcn-svelte, the Svelte port of shadcn/ui. This skill covers component installation, customization, and integration patterns for Svelte and SvelteKit projects using Tailwind CSS v4.1 with the @tailwindcss/vite plugin.

When to Use

Use shadcn-svelte when:

  • Building component-based Svelte/SvelteKit applications

  • You need accessible, styled UI elements (buttons, forms, modals, dialogs, etc.)

  • You want customizable components that don't lock you into a UI library

  • You're using Tailwind CSS v4.1 with Vite for zero-runtime CSS

  • You need complex components like data tables, drawers, or navigation menus

  • You want TypeScript support with full type safety

Do NOT use for lightweight static sites or when you prefer minimal dependencies.

Svelte Component Library Ecosystem

While this skill focuses on shadcn-svelte, here's the broader Svelte UI landscape to help you choose:

Library Comparison

Library Type Best For Learning Curve

shadcn-svelte Copy-paste components Full customization, TypeScript-first Medium

Skeleton UI Installable package Rapid development, themes Low

Melt UI Headless primitives Maximum accessibility control High

Custom Built from scratch Unique requirements Varies

Skeleton UI

Full-featured component library with built-in theming:

npm install @skeletonlabs/skeleton @skeletonlabs/tw-plugin

<script> import { AppBar, AppShell } from '@skeletonlabs/skeleton'; </script>

<AppShell> <svelte:fragment slot="header"> <AppBar>My App</AppBar> </svelte:fragment> <slot /> </AppShell>

Use when: You want rapid development with pre-built themes and don't need deep customization.

Melt UI

Headless, accessible primitives—you bring the styling:

npm install @melt-ui/svelte

<script> import { createDialog, melt } from '@melt-ui/svelte';

const { trigger, overlay, content, title, close } = createDialog(); </script>

<button use:melt={$trigger}>Open</button>

{#if $open} <div use:melt={$overlay} class="fixed inset-0 bg-black/50" /> <div use:melt={$content} class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg"> <h2 use:melt={$title}>Dialog Title</h2> <button use:melt={$close}>Close</button> </div> {/if}

Use when: You need complete styling control with guaranteed accessibility.

Quick Add Workflow

For simple component additions across libraries:

shadcn-svelte (recommended for this skill)

npx shadcn-svelte@latest add button card dialog

Skeleton UI

npm install @skeletonlabs/skeleton

Melt UI

npm install @melt-ui/svelte

For complex multi-component features, see the workflows.md reference document.

Core Concepts

shadcn-svelte is copy-paste component infrastructure, not a traditional npm package. You own the code—components live in your $lib/components/ui/ directory. This means:

  • Full customization without forking

  • No version lock-in (upgrade on your schedule)

  • TypeScript-first with Svelte 5 reactive variables

  • Tailwind v4.1 with @tailwindcss/vite for zero-runtime styling

  • Built on Bits UI primitives for accessibility

  • CSS variables for dynamic theming

Setup: SvelteKit with Tailwind v4.1

  1. Create SvelteKit Project

pnpm dlx sv create my-app cd my-app

  1. Install Tailwind v4.1 + @tailwindcss/vite

pnpm i -D tailwindcss @tailwindcss/vite pnpm dlx shadcn-svelte@latest init

  1. Configure Vite with Tailwind Plugin

Edit vite.config.ts :

import { defineConfig } from 'vite' import { sveltekit } from '@sveltejs/kit/vite' import tailwindcss from '@tailwindcss/vite'

export default defineConfig({ plugins: [tailwindcss(), sveltekit()], })

  1. Update CSS Entry Point

Edit src/app.css (remove old @tailwind directives):

@import "tailwindcss";

/* Your custom CSS here */ @layer utilities { .btn-custom { @apply px-4 py-2 rounded-lg font-semibold transition-colors; } }

No separate tailwind.config.js needed—Tailwind v4.1 scans content automatically. If you need custom configuration:

// tailwind.config.js (optional) export default { theme: { extend: { colors: { brand: { 50: '#f9fafb', // ... }, }, }, }, plugins: [], }

  1. Ensure CSS is Imported in Layout

Edit src/routes/+layout.svelte :

<script> import '../app.css' </script>

<slot />

The init command:

  • Sets up @tailwindcss/vite plugin in vite.config.ts

  • Creates src/app.css with @import "tailwindcss"

  • Creates the cn() utility function for class merging

  • Configures path aliases

Add Components

Add individual components

pnpm dlx shadcn-svelte@latest add button

Add multiple at once

pnpm dlx shadcn-svelte@latest add card alert dialog

Add all components

pnpm dlx shadcn-svelte@latest add --all

View all available components

pnpm dlx shadcn-svelte@latest list

Components install to: src/lib/components/ui/[component-name]/

Common Workflows

Basic Component Usage

<script lang="ts"> import { Button } from "$lib/components/ui/button"; </script>

<Button variant="default">Click me</Button> <Button variant="outline">Outlined</Button> <Button variant="destructive">Delete</Button> <Button disabled>Disabled</Button>

Form with Validation

pnpm dlx shadcn-svelte@latest add form input label

<script lang="ts"> import { superForm } from "sveltekit-superforms"; import { zodClient } from "sveltekit-superforms/adapters"; import { z } from "zod"; import * as Form from "$lib/components/ui/form"; import { Input } from "$lib/components/ui/input"; import { Button } from "$lib/components/ui/button";

const schema = z.object({ email: z.string().email(), name: z.string().min(2), });

const form = superForm(data.form, { validators: zodClient(schema), });

const { form: formData, enhance } = form; </script>

<form method="POST" use:enhance> <Form.Field {form} name="email"> <Form.Control let:attrs> <Form.Label>Email</Form.Label> <Input {...attrs} type="email" bind:value={$formData.email} /> </Form.Control> <Form.FieldErrors /> </Form.Field>

<Button type="submit">Submit</Button> </form>

Data Table with TanStack Table v8

For production data tables, use TanStack Table v8 (not svelte-headless-table) for advanced features like sorting, filtering, pagination, and row selection.

Installation

Add table components and helpers

pnpm dlx shadcn-svelte@latest add table data-table button dropdown-menu checkbox input

Install TanStack Table core

pnpm add @tanstack/table-core

Project Structure for Data Tables

routes/your-route/ columns.ts # Column definitions data-table.svelte # Main table component data-table-actions.svelte # Row action menus data-table-checkbox.svelte # Selection checkboxes data-table-[field]-button.svelte # Sortable headers +page.svelte # Page using the table

Basic Data Table Setup

Step 1: Define Columns (columns.ts )

import type { ColumnDef } from "@tanstack/table-core"; import { renderComponent, renderSnippet } from "$lib/components/ui/data-table/index.js"; import { createRawSnippet } from "svelte";

export type Payment = { id: string; amount: number; status: "pending" | "processing" | "success" | "failed"; email: string; };

export const columns: ColumnDef<Payment>[] = [ // Simple text column { accessorKey: "status", header: "Status", },

// Formatted cell with snippet { accessorKey: "amount", header: () => { const snippet = createRawSnippet(() => ({ render: () => &#x3C;div class="text-end">Amount&#x3C;/div>, })); return renderSnippet(snippet); }, cell: ({ row }) => { const snippet = createRawSnippet<[{ value: number }]>((getValue) => { const { value } = getValue(); const formatted = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", }).format(value); return { render: () => &#x3C;div class="text-end font-medium">${formatted}&#x3C;/div>, }; }); return renderSnippet(snippet, { value: row.original.amount }); }, },

// Component-based cell (for complex UI like action menus) { id: "actions", cell: ({ row }) => renderComponent(DataTableActions, { payment: row.original }), }, ];

Step 2: Create Table Component (data-table.svelte )

<script lang="ts" generics="TData, TValue"> import { type ColumnDef, type PaginationState, type SortingState, getCoreRowModel, getPaginationRowModel, getSortedRowModel, } from "@tanstack/table-core"; import { createSvelteTable, FlexRender } from "$lib/components/ui/data-table/index.js"; import * as Table from "$lib/components/ui/table/index.js"; import { Button } from "$lib/components/ui/button/index.js";

type DataTableProps<TData, TValue> = { data: TData[]; columns: ColumnDef<TData, TValue>[]; };

let { data, columns }: DataTableProps<TData, TValue> = $props();

// State management with Svelte 5 runes let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 }); let sorting = $state<SortingState>([]);

const table = createSvelteTable({ get data() { return data; }, columns, state: { get pagination() { return pagination; }, get sorting() { return sorting; }, }, onPaginationChange: (updater) => { pagination = typeof updater === "function" ? updater(pagination) : updater; }, onSortingChange: (updater) => { sorting = typeof updater === "function" ? updater(sorting) : updater; }, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), getSortedRowModel: getSortedRowModel(), }); </script>

<div class="w-full"> <div class="rounded-md border"> <Table.Root> <Table.Header> {#each table.getHeaderGroups() as headerGroup (headerGroup.id)} <Table.Row> {#each headerGroup.headers as header (header.id)} <Table.Head> {#if !header.isPlaceholder} <FlexRender content={header.column.columnDef.header} context={header.getContext()} /> {/if} </Table.Head> {/each} </Table.Row> {/each} </Table.Header> <Table.Body> {#each table.getRowModel().rows as row (row.id)} <Table.Row> {#each row.getVisibleCells() as cell (cell.id)} <Table.Cell> <FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} /> </Table.Cell> {/each} </Table.Row> {:else} <Table.Row> <Table.Cell colspan={columns.length} class="h-24 text-center"> No results. </Table.Cell> </Table.Row> {/each} </Table.Body> </Table.Root> </div>

<!-- Pagination controls --> <div class="flex items-center justify-end space-x-2 pt-4"> <Button variant="outline" size="sm" onclick={() => table.previousPage()} disabled={!table.getCanPreviousPage()} > Previous </Button> <Button variant="outline" size="sm" onclick={() => table.nextPage()} disabled={!table.getCanNextPage()} > Next </Button> </div> </div>

Step 3: Use in Page (+page.svelte )

<script lang="ts"> import DataTable from "./data-table.svelte"; import { columns } from "./columns.js";

let { data } = $props(); </script>

<DataTable data={data.payments} {columns} />

Adding Sorting to Columns

Create sortable header button component (data-table-email-button.svelte ):

<script lang="ts"> import type { ComponentProps } from "svelte"; import ArrowUpDownIcon from "@lucide/svelte/icons/arrow-up-down"; import { Button } from "$lib/components/ui/button/index.js";

let { variant = "ghost", ...restProps }: ComponentProps<typeof Button> = $props(); </script>

<Button {variant} {...restProps}> Email <ArrowUpDownIcon class="ms-2" /> </Button>

Update column definition:

{ accessorKey: "email", header: ({ column }) => renderComponent(DataTableEmailButton, { onclick: column.getToggleSortingHandler(), }), }

Adding Filtering

Add to data-table.svelte :

<script lang="ts" generics="TData, TValue"> import { type ColumnFiltersState, getFilteredRowModel, } from "@tanstack/table-core"; import { Input } from "$lib/components/ui/input/index.js";

let columnFilters = $state<ColumnFiltersState>([]);

const table = createSvelteTable({ // ... existing config state: { get columnFilters() { return columnFilters; }, }, onColumnFiltersChange: (updater) => { columnFilters = typeof updater === "function" ? updater(columnFilters) : updater; }, getFilteredRowModel: getFilteredRowModel(), }); </script>

<!-- Add above table --> <div class="flex items-center py-4"> <Input placeholder="Filter emails..." value={(table.getColumn("email")?.getFilterValue() as string) ?? ""} oninput={(e) => table.getColumn("email")?.setFilterValue(e.currentTarget.value)} onchange={(e) => table.getColumn("email")?.setFilterValue(e.currentTarget.value)} class="max-w-sm" /> </div>

Adding Row Selection

Create checkbox component (data-table-checkbox.svelte ):

<script lang="ts"> import type { ComponentProps } from "svelte"; import { Checkbox } from "$lib/components/ui/checkbox/index.js";

let { checked = false, onCheckedChange = (v) => (checked = v), ...restProps }: ComponentProps<typeof Checkbox> = $props(); </script>

<Checkbox bind:checked={() => checked, onCheckedChange} {...restProps} />

Add select column to columns.ts :

{ id: "select", header: ({ table }) => renderComponent(DataTableCheckbox, { checked: table.getIsAllPageRowsSelected(), indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(), onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value), "aria-label": "Select all", }), cell: ({ row }) => renderComponent(DataTableCheckbox, { checked: row.getIsSelected(), onCheckedChange: (value) => row.toggleSelected(!!value), "aria-label": "Select row", }), enableSorting: false, enableHiding: false, }

Add to data-table.svelte :

<script lang="ts" generics="TData, TValue"> import { type RowSelectionState } from "@tanstack/table-core";

let rowSelection = $state<RowSelectionState>({});

const table = createSvelteTable({ // ... existing config state: { get rowSelection() { return rowSelection; }, }, onRowSelectionChange: (updater) => { rowSelection = typeof updater === "function" ? updater(rowSelection) : updater; }, }); </script>

<!-- Show selection count --> <div class="text-muted-foreground flex-1 text-sm"> {table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected. </div>

Row Actions Pattern

Create actions component (data-table-actions.svelte ):

<script lang="ts"> import EllipsisIcon from "@lucide/svelte/icons/ellipsis"; import { Button } from "$lib/components/ui/button/index.js"; import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";

let { id }: { id: string } = $props(); </script>

<DropdownMenu.Root> <DropdownMenu.Trigger> {#snippet child({ props })} <Button {...props} variant="ghost" size="icon" class="relative size-8 p-0"> <span class="sr-only">Open menu</span> <EllipsisIcon /> </Button> {/snippet} </DropdownMenu.Trigger> <DropdownMenu.Content> <DropdownMenu.Label>Actions</DropdownMenu.Label> <DropdownMenu.Item onclick={() => navigator.clipboard.writeText(id)}> Copy ID </DropdownMenu.Item> <DropdownMenu.Separator /> <DropdownMenu.Item>View details</DropdownMenu.Item> <DropdownMenu.Item>Edit</DropdownMenu.Item> </DropdownMenu.Content> </DropdownMenu.Root>

Styling Data Tables with Tailwind v4.1

Define CSS Variables for Table States in src/app.css :

@import "tailwindcss";

@layer theme { :root { /* Table colors */ --color-table-bg: 0 0% 100%; --color-table-row-hover: 0 0% 96.1%; --color-table-row-selected: 210 40% 96%; --color-table-border: 0 0% 89.8%; --color-table-text: 0 0% 3.6%; --color-table-header-bg: 0 0% 94.1%; }

.dark { --color-table-bg: 0 0% 14.9%; --color-table-row-hover: 0 0% 22%; --color-table-row-selected: 210 100% 35%; --color-table-border: 0 0% 22%; --color-table-text: 0 0% 98%; --color-table-header-bg: 0 0% 22%; } }

@layer utilities { .table-cell { @apply px-4 py-3 text-sm; }

.table-row-hover { @apply hover:bg-[hsl(var(--color-table-row-hover))] transition-colors; }

.table-row-selected { @apply bg-[hsl(var(--color-table-row-selected))] border-l-4 border-l-primary; }

.table-header { @apply bg-[hsl(var(--color-table-header-bg))] font-semibold text-xs uppercase tracking-wide; } }

Apply Row States in your table component:

<Table.Body> {#each table.getRowModel().rows as row (row.id)} {@const rowSelected = row.getIsSelected()} <Table.Row class={cn( "table-row-hover", rowSelected && "table-row-selected" )} data-state={rowSelected && "selected"} > {#each row.getVisibleCells() as cell (cell.id)} <Table.Cell class="table-cell"> <FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} /> </Table.Cell> {/each} </Table.Row> {/each} </Table.Body>

Key Patterns for TanStack Tables

  • Svelte 5 State Management: Always use $state and get accessors

  • State Updater Pattern: All handlers follow (updater) => state = typeof updater === "function" ? updater(state) : updater

  • Cell Rendering:

  • Simple HTML: createRawSnippet → renderSnippet

  • Components: renderComponent for interactive UI

  • Plain text: Direct string or number

  • Row Models: Add the appropriate row model for each feature (pagination, sorting, filtering)

Common Pitfalls

  • Forgetting get accessors in createSvelteTable state config

  • Not binding both oninput and onchange for filter inputs

  • Missing row models (e.g., getFilteredRowModel for filtering)

  • Using wrong import path for data-table helpers

For comprehensive DataTable reference including:

  • Advanced sorting and filtering patterns

  • Column visibility controls

  • Responsive table layouts

  • Performance optimization (virtual scrolling, debouncing)

  • Complete working examples with all features

See: shadcn-datatable.md and datatable-tanstack-svelte5.md reference documents

Modal/Dialog

pnpm dlx shadcn-svelte@latest add dialog button

<script lang="ts"> import * as Dialog from "$lib/components/ui/dialog"; import { Button } from "$lib/components/ui/button";

let open = false; </script>

<Dialog.Root bind:open> <Dialog.Trigger asChild let:builder> <Button builders={[builder]}>Open Dialog</Button> </Dialog.Trigger> <Dialog.Content> <Dialog.Header> <Dialog.Title>Dialog Title</Dialog.Title> <Dialog.Description>This is a dialog.</Dialog.Description> </Dialog.Header> <p>Your content here</p> <Dialog.Footer> <Button on:click={() => (open = false)}>Close</Button> </Dialog.Footer> </Dialog.Content> </Dialog.Root>

Drawer (Mobile-Friendly Sidebar)

pnpm dlx shadcn-svelte@latest add drawer button

<script lang="ts"> import * as Drawer from "$lib/components/ui/drawer"; import { Button } from "$lib/components/ui/button";

let open = false; </script>

<Drawer.Root bind:open> <Drawer.Trigger asChild let:builder> <Button builders={[builder]} variant="outline">Open Drawer</Button> </Drawer.Trigger> <Drawer.Content> <Drawer.Header> <Drawer.Title>Navigation</Drawer.Title> </Drawer.Header> <nav class="flex flex-col gap-2 p-4"> <a href="/">Home</a> <a href="/about">About</a> </nav> <Drawer.Footer> <Drawer.Close asChild let:builder> <Button builders={[builder]} variant="outline">Close</Button> </Drawer.Close> </Drawer.Footer> </Drawer.Content> </Drawer.Root>

Component Organization

Project Structure

src/ ├── lib/ │ ├── components/ │ │ ├── ui/ (shadcn-svelte components) │ │ │ ├── button/ │ │ │ ├── card/ │ │ │ ├── dialog/ │ │ │ └── [...] │ │ └── custom/ (your custom components) │ │ └── header.svelte │ └── utils/ │ └── cn.ts (class utility from init) └── routes/

Import Patterns

// Named imports (preferred for tree-shaking) import { Button } from "$lib/components/ui/button"; import { Dialog, DialogTrigger, DialogContent } from "$lib/components/ui/dialog";

// Namespace imports import * as Button from "$lib/components/ui/button"; import * as Dialog from "$lib/components/ui/dialog";

Customization

Theme via CSS Variables (Tailwind v4.1)

Edit src/app.css to define theme colors as CSS variables. Tailwind v4.1 auto-scans these:

@import "tailwindcss";

@layer theme { :root { --color-background: 0 0% 100%; --color-foreground: 0 0% 3.6%; --color-primary: 0 0% 9%; --color-primary-foreground: 0 0% 100%; --color-secondary: 0 0% 96.1%; --color-secondary-foreground: 0 0% 9%; --color-destructive: 0 84% 60%; --color-muted: 0 0% 96.1%; --color-muted-foreground: 0 0% 45.1%; --color-border: 0 0% 89.8%; }

.dark { --color-background: 0 0% 3.6%; --color-foreground: 0 0% 98%; --color-primary: 0 0% 98%; --color-primary-foreground: 0 0% 9%; --color-secondary: 0 0% 14.9%; --color-secondary-foreground: 0 0% 98%; --color-destructive: 0 84% 60%; --color-muted: 0 0% 14.9%; --color-muted-foreground: 0 0% 63.9%; --color-border: 0 0% 14.9%; } }

@layer utilities { .btn-custom { @apply px-4 py-2 rounded-lg font-semibold transition-colors; } }

Reference in tailwind.config.js (if needed):

export default { theme: { colors: { background: 'hsl(var(--color-background))', foreground: 'hsl(var(--color-foreground))', primary: 'hsl(var(--color-primary))', 'primary-foreground': 'hsl(var(--color-primary-foreground))', // ... map CSS variables to theme }, extend: { spacing: { gutter: '1rem', }, }, }, }

Override Component Styles

Modify components in src/lib/components/ui/[name]/ directly. Tailwind v4.1 automatically applies classes:

<!-- src/lib/components/ui/button/button.svelte --> <script lang="ts"> import { cn } from "$lib/utils";

interface Props { variant?: "default" | "outline" | "ghost"; size?: "sm" | "md" | "lg"; class?: string; }

let { variant = "default", size = "md", class: className }: Props = $props();

const baseClasses = cn( "inline-flex items-center justify-center font-semibold transition-colors", { "bg-primary text-primary-foreground hover:bg-primary/90": variant === "default", "border border-border bg-background hover:bg-muted": variant === "outline", "hover:bg-muted": variant === "ghost", }, { "h-8 px-3 text-xs": size === "sm", "h-10 px-4 text-sm": size === "md", "h-12 px-6 text-base": size === "lg", }, className ); </script>

<button class={baseClasses}> <slot /> </button>

Tailwind v4.1 Content Scanning

No manual content paths needed—Tailwind v4.1 auto-scans your SvelteKit project:

// tailwind.config.js (minimal, Vite handles scanning) export default { theme: { extend: { colors: { brand: { 50: '#f9fafb', 600: '#1f2937', }, }, }, }, }

For custom @source directive in CSS:

@import "tailwindcss";

@source "../src/lib/components";

Key Dependencies

Automatically installed with init and Tailwind v4.1:

Package Purpose

@tailwindcss/vite

Vite plugin for zero-runtime CSS with v4.1

tailwindcss

Tailwind CSS framework (v4.1+)

clsx / tailwind-merge

Class utility functions via cn()

@lucide/svelte

Icon library (650+ icons)

bits-ui

Headless UI primitives (accessibility)

sveltekit

Full-stack framework

vite

Build tool (handles CSS imports)

Not needed in v4.1:

  • PostCSS

  • Autoprefixer

  • tailwind.config.ts for basic projects

Advanced Topics

Dark Mode

Use mode-watcher for automatic dark mode switching:

pnpm i mode-watcher

<script lang="ts"> import { modeWatcher } from "mode-watcher"; </script>

<div use:modeWatcher> <!-- Your app content --> </div>

Icons with Lucide

pnpm dlx shadcn-svelte@latest add button

<script lang="ts"> import { Button } from "$lib/components/ui/button"; import { Heart } from "@lucide/svelte"; </script>

<Button> <Heart class="w-4 h-4 mr-2" /> Save </Button>

Creating Custom Components

Copy shadcn component structure as template:

<!-- src/lib/components/custom/my-card.svelte --> <script lang="ts"> import { cn } from "$lib/utils";

interface Props { title: string; class?: string; }

let { title, class: className, children }: Props & { children?: any } = $props(); </script>

<div class={cn("rounded-lg border p-4", className)}> <h3>{title}</h3> {#if children} <slot /> {/if} </div>

Building Component Registries

To create a custom registry for sharing components:

// registry.json { "$schema": "https://shadcn-svelte.com/schema/registry.json", "name": "my-components", "homepage": "https://my-components.com", "items": [ { "name": "custom-card", "type": "registry:component", "title": "Custom Card", "description": "Extended card component", "files": [ { "path": "./src/lib/custom-card.svelte", "type": "registry:component" } ] } ] }

Build registry: pnpm run registry:build

Troubleshooting

Component Not Found

Verify install location: src/lib/components/ui/[component]/

pnpm dlx shadcn-svelte@latest list # Check installed components pnpm dlx shadcn-svelte@latest add button --overwrite # Reinstall

Styling Issues

  • Ensure Tailwind CSS is configured in tailwind.config.ts

  • Check that CSS variables are defined in src/app.css

  • Verify component imports use correct path aliases

TypeScript Errors

Update TypeScript settings in svelte.config.js :

const config: Config = { kit: { alias: { "$lib": "./src/lib", "$components": "./src/lib/components", }, }, };

Command and Hook

/shadcn Command

Interactive assistant for shadcn-svelte component development:

/shadcn # Show help and available topics /shadcn add # Component installation guidance /shadcn form # Form patterns with superforms /shadcn table # DataTable with TanStack Table v8 /shadcn dialog # Modal/drawer/sheet patterns /shadcn theme # CSS variables and customization /shadcn debug # Troubleshooting common issues /shadcn button # Specific component guidance

Development Hook

A hook is available that triggers when editing files in $lib/components/ui/ :

Install hook script:

Script location: ~/.claude/hooks/shadcn-component-reminder.sh

chmod +x ~/.claude/hooks/shadcn-component-reminder.sh

Add to ~/.claude/settings.json:

{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "~/.claude/hooks/shadcn-component-reminder.sh", "timeout": 5 } ] } ] } }

The hook provides contextual reminders about shadcn patterns when editing component files.

Resources

Summary

shadcn-svelte provides production-grade UI components that you control. Start with init , add components via CLI, customize in-place, and build accessible applications with TypeScript and Tailwind. The copy-paste model means you own your UI layer—no library lock-in.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

geospatial-postgis-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

rbac-authorization-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

quickbooks-online-api

No summary provided by upstream source.

Repository SourceNeeds Review
General

slack-block-kit

No summary provided by upstream source.

Repository SourceNeeds Review