Get Started
Migration
Components
- Accordion
- Alert Dialog
- Alert
- Aspect Ratio
- Avatar
- Badge
- Breadcrumb
- Button
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Formsnap
- Hover Card
- Input OTP
- Input
- Label
- Menubar
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Range Calendar
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Switch
- Table
- Tabs
- Textarea
- Toggle Group
- Toggle
- Tooltip
- Typography
Installation
Special sponsor
We're looking for one partner to be featured here.
Support the project and reach thousands of developers.
Reach out<script lang="ts">
import MinusIcon from "@lucide/svelte/icons/minus";
import PlusIcon from "@lucide/svelte/icons/plus";
import * as Drawer from "$lib/components/ui/drawer/index.js";
import { Button, buttonVariants } from "$lib/components/ui/button/index.js";
import { BarChart, type ChartContextValue } from "layerchart";
import { scaleBand } from "d3-scale";
import { cubicInOut } from "svelte/easing";
const data = [
{
goal: 400
},
{
goal: 300
},
{
goal: 200
},
{
goal: 300
},
{
goal: 200
},
{
goal: 278
},
{
goal: 189
},
{
goal: 239
},
{
goal: 300
},
{
goal: 200
},
{
goal: 278
},
{
goal: 189
},
{
goal: 349
}
];
let goal = $state(350);
function handleClick(adjustment: number) {
goal = Math.max(200, Math.min(400, goal + adjustment));
}
let context = $state<ChartContextValue>();
</script>
<Drawer.Root>
<Drawer.Trigger class={buttonVariants({ variant: "outline" })}
>Open Drawer</Drawer.Trigger
>
<Drawer.Content>
<div class="mx-auto w-full max-w-sm">
<Drawer.Header>
<Drawer.Title>Move Goal</Drawer.Title>
<Drawer.Description>Set your daily activity goal.</Drawer.Description>
</Drawer.Header>
<div class="p-4 pb-0">
<div class="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
class="size-8 shrink-0 rounded-full"
onclick={() => handleClick(-10)}
disabled={goal <= 200}
>
<MinusIcon />
<span class="sr-only">Decrease</span>
</Button>
<div class="flex-1 text-center">
<div class="text-7xl font-bold tracking-tighter">
{goal}
</div>
<div class="text-muted-foreground text-[0.70rem] uppercase">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
class="size-8 shrink-0 rounded-full"
onclick={() => handleClick(10)}
disabled={goal >= 400}
>
<PlusIcon />
<span class="sr-only">Increase</span>
</Button>
</div>
<div class="mt-3 h-[120px]">
<div class="h-full w-full">
<BarChart
bind:context
data={data.map((d, i) => ({ goal: d.goal, index: i }))}
y="goal"
x="index"
xScale={scaleBand().padding(0.25)}
axis={false}
tooltip={false}
props={{
bars: {
stroke: "none",
rounded: "all",
radius: 4,
// use the height of the chart to animate the bars
initialY: context?.height,
initialHeight: 0,
motion: {
x: { type: "tween", duration: 500, easing: cubicInOut },
width: { type: "tween", duration: 500, easing: cubicInOut },
height: {
type: "tween",
duration: 500,
easing: cubicInOut
},
y: { type: "tween", duration: 500, easing: cubicInOut }
},
fill: "var(--color-foreground)",
fillOpacity: 0.9
},
highlight: { area: { fill: "none" } }
}}
/>
</div>
</div>
</div>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close class={buttonVariants({ variant: "outline" })}
>Cancel</Drawer.Close
>
</Drawer.Footer>
</div>
</Drawer.Content>
</Drawer.Root>
About
Drawer is built on top of Vaul Svelte, which is a Svelte port of Vaul by Emil Kowalski.
Installation
pnpm dlx shadcn-svelte@latest add drawer
npx shadcn-svelte@latest add drawer
bun x shadcn-svelte@latest add drawer
npx shadcn-svelte@latest add drawer
Install vaul-svelte
:
pnpm i vaul-svelte@next -D
npm i vaul-svelte@next -D
bun install vaul-svelte@next -D
yarn install vaul-svelte@next -D
Copy and paste the component source files linked at the top of this page into your project.
Usage
<script lang="ts">
import * as Drawer from "$lib/components/ui/drawer/index.js";
</script>
<Drawer.Root>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Are you sure absolutely sure?</Drawer.Title>
<Drawer.Description>This action cannot be undone.</Drawer.Description>
</Drawer.Header>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close>Cancel</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
Examples
Responsive Dialog
You can combine the Dialog
and Drawer
components to create a responsive dialog. This renders a Dialog
on desktop and a Drawer
on mobile.
<script lang="ts">
import { MediaQuery } from "svelte/reactivity";
import * as Dialog from "$lib/components/ui/dialog/index.js";
import * as Drawer from "$lib/components/ui/drawer/index.js";
import { Input } from "$lib/components/ui/input/index.js";
import { Label } from "$lib/components/ui/label/index.js";
import { Button, buttonVariants } from "$lib/components/ui/button/index.js";
let open = $state(false);
const isDesktop = new MediaQuery("(min-width: 768px)");
const id = $props.id();
</script>
{#if isDesktop.current}
<Dialog.Root bind:open>
<Dialog.Trigger class={buttonVariants({ variant: "outline" })}
>Edit Profile</Dialog.Trigger
>
<Dialog.Content class="sm:max-w-[425px]">
<Dialog.Header>
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
Make changes to your profile here. Click save when you're done.
</Dialog.Description>
</Dialog.Header>
<form class="grid items-start gap-4">
<div class="grid gap-2">
<Label for="email-{id}">Email</Label>
<Input type="email" id="email-{id}" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username-{id}">Username</Label>
<Input id="username-{id}" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
</Dialog.Content>
</Dialog.Root>
{:else}
<Drawer.Root bind:open>
<Drawer.Trigger class={buttonVariants({ variant: "outline" })}
>Edit Profile</Drawer.Trigger
>
<Drawer.Content>
<Drawer.Header class="text-left">
<Drawer.Title>Edit profile</Drawer.Title>
<Drawer.Description>
Make changes to your profile here. Click save when you're done.
</Drawer.Description>
</Drawer.Header>
<form class="grid items-start gap-4 px-4">
<div class="grid gap-2">
<Label for="email-{id}">Email</Label>
<Input type="email" id="email-{id}" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username-{id}">Username</Label>
<Input id="username-{id}" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
<Drawer.Footer class="pt-2">
<Drawer.Close class={buttonVariants({ variant: "outline" })}
>Cancel</Drawer.Close
>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
{/if}