Docs
  Drawer
Drawer
A drawer component for Svelte.
	<script lang="ts">
  import Minus from "lucide-svelte/icons/minus";
  import Plus from "lucide-svelte/icons/plus";
  import { VisGroupedBar, VisXYContainer } from "@unovis/svelte";
  import {
    Button,
    buttonVariants
  } from "$lib/components/ui/button/index.js";
  import * as Drawer from "$lib/components/ui/drawer/index.js";
 
  const data = [
    {
      id: 1,
      goal: 400
    },
    {
      id: 2,
      goal: 300
    },
    {
      id: 3,
      goal: 200
    },
    {
      id: 4,
      goal: 300
    },
    {
      id: 5,
      goal: 200
    },
    {
      id: 6,
      goal: 278
    },
    {
      id: 7,
      goal: 189
    },
    {
      id: 8,
      goal: 239
    },
    {
      id: 9,
      goal: 300
    },
    {
      id: 10,
      goal: 200
    },
    {
      id: 11,
      goal: 278
    },
    {
      id: 12,
      goal: 189
    },
    {
      id: 13,
      goal: 349
    }
  ];
  const x = (d: { goal: number; id: number }) => d.id;
  const y = (d: { goal: number; id: number }) => d.goal;
 
  let goal = 350;
 
  function handleClick(adjustment: number) {
    goal = Math.max(200, Math.min(400, goal + adjustment));
  }
</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}
          >
            <Minus />
            <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)}
          >
            <Plus />
            <span class="sr-only">Increase</span>
          </Button>
        </div>
        <div class="mt-3 h-[120px]">
          <VisXYContainer {data} height={60}>
            <VisGroupedBar {x} {y} color="hsl(var(--primary) / 0.2)" />
          </VisXYContainer>
        </div>
      </div>
      <Drawer.Footer>
        <Button>Submit</Button>
        <Drawer.Close class={buttonVariants({ variant: "outline" })}
          >Cancel</Drawer.Close
        >
      </Drawer.Footer>
    </div>
  </Drawer.Content>
</Drawer.Root>
  
	<script lang="ts">
  import Minus from "lucide-svelte/icons/minus";
  import Plus from "lucide-svelte/icons/plus";
  import { VisGroupedBar, VisXYContainer } from "@unovis/svelte";
  import * as Drawer from "$lib/components/ui/drawer/index.js";
  import {
    Button,
    buttonVariants
  } from "$lib/components/ui/button/index.js";
 
  const data = [
    {
      id: 1,
      goal: 400
    },
    {
      id: 2,
      goal: 300
    },
    {
      id: 3,
      goal: 200
    },
    {
      id: 4,
      goal: 300
    },
    {
      id: 5,
      goal: 200
    },
    {
      id: 6,
      goal: 278
    },
    {
      id: 7,
      goal: 189
    },
    {
      id: 8,
      goal: 239
    },
    {
      id: 9,
      goal: 300
    },
    {
      id: 10,
      goal: 200
    },
    {
      id: 11,
      goal: 278
    },
    {
      id: 12,
      goal: 189
    },
    {
      id: 13,
      goal: 349
    }
  ];
 
  const x = (d: { goal: number; id: number }) => d.id;
  const y = (d: { goal: number; id: number }) => d.goal;
 
  let goal = $state(350);
 
  function handleClick(adjustment: number) {
    goal = Math.max(200, Math.min(400, goal + adjustment));
  }
</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}
          >
            <Minus />
            <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}
          >
            <Plus />
            <span class="sr-only">Increase</span>
          </Button>
        </div>
        <div class="mt-3 h-[120px]">
          <VisXYContainer {data} height={60}>
            <VisGroupedBar {x} {y} color="hsl(var(--primary) / 0.2)" />
          </VisXYContainer>
        </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
	npx shadcn-svelte@next add drawer
   Install vaul-svelte:
	npm i vaul-svelte -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)");
</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">Email</Label>
          <Input type="email" id="email" value="shadcn@example.com" />
        </div>
        <div class="grid gap-2">
          <Label for="username">Username</Label>
          <Input id="username" 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">Email</Label>
          <Input type="email" id="email" value="shadcn@example.com" />
        </div>
        <div class="grid gap-2">
          <Label for="username">Username</Label>
          <Input id="username" 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}
  
	<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 = false;
  const isDesktop = new MediaQuery("(min-width: 768px)");
</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">Email</Label>
          <Input type="email" id="email" value="shadcn@example.com" />
        </div>
        <div class="grid gap-2">
          <Label for="username">Username</Label>
          <Input id="username" 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">Email</Label>
          <Input type="email" id="email" value="shadcn@example.com" />
        </div>
        <div class="grid gap-2">
          <Label for="username">Username</Label>
          <Input id="username" 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}
  On This Page