Installation
How to install dependencies and structure your app.
Unlike the original shadcn/ui for React, where the full components can exist in a single file, components in this port are split into multiple files. This is because Svelte doesn't support defining multiple components in a single file, so utilizing the CLI to add components will be the optimal approach.
The CLI will create a folder for each component, which will sometimes just contain a single Svelte file, and in other times, multiple files. Within each folder, there will be an index.ts
file that exports the component(s), so you can import them from a single file.
For example, the Accordion component is split into four .svelte
files:
Accordion.svelte
AccordionContent.svelte
AccordionItem.svelte
AccordionTrigger.svelte
They can then be imported from the accordion/index.ts
file like so:
import * as Accordion from '$lib/components/ui/accordion"
// or
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger
} from "$lib/components/ui/accordion"
Regardless of the import approach you take, the components will be tree-shaken by Rollup, so you don't have to worry about unused components being bundled into your app.
New Project
Create project
Use the SvelteKit CLI to create a new project.
npm create svelte@latest my-app
Add TailwindCSS
Use the svelte-add
CLI to add Tailwind CSS to your project.
npx svelte-add@latest tailwindcss
Install dependencies
npm install
Setup path aliases
If you are using SvelteKit and are not using the default alias $lib
, you'll need to update your svelte.config.js
file to include those aliases.
const config = {
// ... other config
kit: {
// ... other config
alias: {
"@/*": "./path/to/lib/*",
},
},
};
If you are not using SvelteKit, then you'll need to update your path aliases in your tsconfig.json
and vite.config.ts
.
{
"compilerOptions": {
// ... other options
"paths": {
"$lib": ["./src/lib"],
"$lib/*": ["./src/lib/*"]
}
}
}
import path from "path";
export default defineConfig({
// ... other options
resolve: {
alias: {
$lib: path.resolve("./src/lib"),
},
},
});
Run the CLI
npx shadcn-svelte@latest init
Configure components.json
You will be asked a few questions to configure components.json
:
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › src/app.pcss
Where is your tailwind.config.[cjs|js|ts] located? › tailwind.config.js
Configure the import alias for components: › $lib/components
Configure the import alias for utils: › $lib/utils
Manual Installation
Create project
Use the SvelteKit CLI to create a new project.
npm create svelte@latest my-app
Add Tailwind
Use the svelte-add
CLI to add Tailwind CSS to your project.
npx svelte-add@latest tailwindcss
Add dependencies
Add the following dependencies to your project:
npm install tailwind-variants clsx tailwind-merge
Configure tailwind.config.js
This is what this project's tailwind.config.js
file looks like:
import { fontFamily } from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
const config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
background: "hsl(var(--background) / <alpha-value>)",
foreground: "hsl(var(--foreground) / <alpha-value>)",
primary: {
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
foreground: "hsl(var(--primary-foreground) / <alpha-value>)",
},
secondary: {
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)",
},
destructive: {
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
},
muted: {
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
foreground: "hsl(var(--muted-foreground) / <alpha-value>)",
},
accent: {
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
foreground: "hsl(var(--accent-foreground) / <alpha-value>)",
},
popover: {
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
foreground: "hsl(var(--popover-foreground) / <alpha-value>)",
},
card: {
DEFAULT: "hsl(var(--card) / <alpha-value>)",
foreground: "hsl(var(--card-foreground) / <alpha-value>)",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["Inter", ...fontFamily.sans],
},
},
},
};
export default config;
Feel free to add or modify as needed to suit your project.
Configure styles
Add the following to your src/app.pcss
file. You can learn more about using CSS variables for theming in the theming section.
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 92% 38%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 359 51% 48%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
}
Configure utils
You'll want to create a cn
helper to make it easier to conditionally add Tailwind CSS classes. Additionally, you'll want to add the custom transition that is used by various components.
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { cubicOut } from "svelte/easing";
import type { TransitionConfig } from "svelte/transition";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export const flyAndScale = (
node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
const scaleConversion = (
valueA: number,
scaleA: [number, number],
scaleB: [number, number]
) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (
style: Record<string, number | string | undefined>
): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + key + ":" + style[key] + ";";
}, "");
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform:
transform +
"translate3d(" +
x +
"px, " +
y +
"px, 0) scale(" +
scale +
")",
opacity: t,
});
},
easing: cubicOut,
};
};
Import styles to your app
Create src/routes/+layout.svelte
and import the styles:
<script lang="ts">
import "../app.pcss";
</script>
<slot />
Icons
This project uses icons from Lucide for the default
style, and Radix for the new-york
style, but feel free to use any icon library.
App structure
Here's a recommended, but not required app structure:
src
├── lib
│ ├── components
│ │ ├── ui
│ │ │ ├── alert-dialog
│ │ │ │ ├── index.ts
│ │ │ │ └── alert.svelte
│ │ │ ├── button
│ │ │ │ ├── index.ts
│ │ │ │ └── button.svelte
│ │ │ └── ...
│ │ ├── navigation.svelte
│ │ ├── page-header.svelte
│ │ └── ...
│ └── utils.ts
├── routes
│ ├── +page.svelte
│ └── +layout.svelte
├── app.pcss
- Place the UI components in the
lib/components/ui
folder. - The rest of the components such as
<PageHeader />
and<Navigation />
are placed in thelib/components
folder. - The
lib/utils.ts
file is where you can define thecn
helper. - The
app.pcss
file contains the global CSS.
That's it. You can now start adding components to your project.
ESLint configuration
If you are using ESLint to find problems in your code, some components might trigger false positives depending on your ESLint configuration. For example, you could end up with lint errors when components define $$Props
to specify the type for restProps
because $$Props
is not directly used in the rest of the component.
To ignore these linting errors, you can modify your ESLint configuration.
One option is to add a .eslintrc
file in the directory where you define your components, for example $lib/components/ui
:
{
"rules": {
"@typescript-eslint/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^\$\$(Props|Events|Slots|Generic)$"
}
]
}
}
The main benefit with adding an additional .eslintrc
file just to $lib/components/ui
is that you will not affect how ESLint functions for the rest of your project. Only your shadcn-svelte
components will ignore these false positives.
If this is not important to you, then another option is to adapt a similar rule override in your global ESLint configuration file, usually .eslintrc.cjs
. For inspiration, please refer this gist.
VSCode extension
Install the shadcn-svelte VSCode extension by @selemondev in Visual Studio Code to easily add Shadcn Svelte components to your project.
This extension offers a range of features:
- Ability to initialize the shadcn-svelte CLI
- Add components to your project
- Navigate to a specific component's documentation page directly from your IDE
- Handy snippets for quick component imports and markup
On This Page