Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Components

Free

Sheet for Svelte

Slide a panel in from a screen edge for a form or a detail, and leave the page in sight. For Svelte with Ark UI, when a centered dialog would hide too much.

Framework
UI library

The panel is anchored to one edge of the screen, rather than floating over the center.

Use it when the task needs its own surface and the page underneath should stay in sight. A short confirmation fits better in a Dialog. In Svelte, Ark UI traps focus while the panel is open, and it can sit on any edge, including the bottom on a narrow screen.

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Sheet anatomy: content, close, title, description, footer
SheetTrigger
SheetOverlay
SheetPositioner
SheetContent
SheetTitle
SheetDescription
SheetClose

Usage

<script lang="ts">
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
  Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter,
  SheetHeader, SheetTitle, SheetTrigger,
} from "@/components/ui/sheet";
</script>
<Sheet>
  <SheetTrigger>
    {#snippet asChild(props)}
      <Button {...props()} variant="outline">Open</Button>
    {/snippet}
  </SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Edit profile</SheetTitle>
      <SheetDescription>
        Make changes to your profile here. Click save when you&apos;re done.
      </SheetDescription>
    </SheetHeader>
    <div class="grid flex-1 auto-rows-min gap-6 px-4">
      <Field class="grid gap-3">
        <FieldLabel>Name</FieldLabel>
        <Input defaultValue="Pedro Duarte" type="text" />
      </Field>
      <Field class="grid gap-3">
        <FieldLabel>Username</FieldLabel>
        <Input defaultValue="@peduarte" type="text" />
      </Field>
    </div>
    <SheetFooter>
      <Button type="submit">Save changes</Button>
      <SheetClose>
        {#snippet asChild(props)}
          <Button {...props()} variant="outline">Close</Button>
        {/snippet}
      </SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>

Accessibility

Enter
Activate the focused control.
Space
Activate the focused control.

Demos

Side

side on SheetContent is top, right, bottom, or left, with right as the default. Top and bottom are capped at half the viewport here, so a long form scrolls inside the panel. Use the bottom edge on a phone, and a side edge for an inspector next to a list. The title, the focus trap, and Escape stay the same on every edge.

Preview width

No Close Button

showCloseButton set to false removes the corner icon. This panel then has a title and a description, and no footer button, so the written hint is to click outside. Hide that icon only when another close control is obvious, usually next to the primary action. Escape still dismisses a modal sheet, and the overlay click is a backup rather than the thing you want people to hunt for.

Preview width

Rtl

The trigger is Arabic, and the panel opens from the left with dir set to rtl on SheetContent. Title, fields, and footer actions align to the start of the line. Put dir on the content, not only the trigger, because the panel is portaled and can miss the page direction. side is still an explicit edge and does not flip on its own.

Preview width

FAQ

How do I control whether a Sheet is open in Svelte?

The sheet starts closed and opens from its trigger. Control it when another part of the page must open or close it. Keep that boolean in state and pass it back, or use the default prop for the first state only. Pass open and read details.open from onOpenChange, and set defaultOpen for the uncontrolled case.

How do I change which edge a Sheet uses?

Set side on SheetContent to top, right, bottom, or left. The default is right, and bottom slides up from the bottom edge, which suits a narrow screen. The Side demo shows all four edges. The sheet still traps focus the same way on every edge.

Why does a Sheet need a SheetTitle?

SheetTitle names the dialog for assistive tech, so always render one. If the design has no visible heading, hide the title with the sr-only utility and keep it in the tree. SheetDescription explains the task and is optional. Escape closes the sheet and returns focus to the trigger.

How do I hide the Sheet close button?

The close button is shown by default. Set showCloseButton to false on SheetContent to hide it, as the No Close Button demo does. Still offer a SheetClose in the footer, or people may not see how to dismiss it. Escape still closes a modal sheet.

How do I use a button as the Sheet trigger with Ark UI?

The trigger should look like your button, not like an extra wrapper. Put the visible label on the button so the control has an accessible name. Put one Button inside SheetTrigger and set asChild, so the trigger merges onto that button.

Should I use a Sheet, a Dialog, or a Drawer?

Use a Sheet when the panel should slide in from a screen edge and leave part of the page in view. Use a Dialog for a centered modal, such as a short confirmation. Use a Drawer when the panel should support swipe, usually from the bottom on a phone.

Why is my Sheet behind another layer, or clipping a long form?

Sheet content is portaled and stacked at z-50, so it sits above the page. If another overlay uses the same layer, edit the sheet file under components/ui and raise the overlay and content together. If a long form is clipped, let the body scroll, for example by wrapping it in a Scroll Area.

How do I install Sheet in Svelte?

Run the command in the Installation section above. It adds Sheet and its parts to your ui folder. Import from that folder when the command finishes, instead of copying the source by hand, and rerun the command later if you want the latest files. The command uses the shadcn-svelte CLI.

API Reference

Sheet

data-slot="sheet"

PropType
open?boolean

The controlled open state of the dialog.

defaultOpen?boolean

The initial open state of the dialog when rendered. Use when you don't need to control the open state of the dialog.

Default
false
onOpenChange?(details: OpenChangeDetails) → void

Function to call when the dialog's open state changes.

Type
(details: OpenChangeDetails) → void
Parameters

details – { open: boolean }

Returns
void
triggerValue?string | null

The controlled active trigger value.

Type
string | null
defaultTriggerValue?string | null

The initial active trigger value when rendered. Use when you don't need to control the active trigger value.

Type
string | null
onTriggerValueChange?(details: TriggerValueChangeDetails) → void

Function to call when the active trigger changes.

Type
(details: TriggerValueChangeDetails) → void
Parameters

details – { value: string | null; triggerElement: HTMLElement | null }

Returns
void
closeOnEscape?boolean

Whether to close the dialog when the escape key is pressed.

Default
true
closeOnInteractOutside?boolean

Whether to close the dialog when the outside is clicked.

Default
true
dir?"ltr" | "rtl"

The document's text/writing direction.

Type
"ltr" | "rtl"
Default
"ltr"
finalFocusEl?() → MaybeElement

Element to receive focus when the dialog is closed.

Returns
MaybeElement
hideMode?"display-none" | "activity"

How to hide content when mounted but not present. - display-none: HTML hidden attribute. Effects stay alive. - activity: React 19 <Activity mode="hidden">. Effects pause. Requires React 19+.

Type
"display-none" | "activity"
Default
"display-none"
id?string

The unique identifier of the machine.

ids?object

The ids of the elements in the dialog. Useful for composition.

Type
{ trigger?: string | ((value?: string) → string); positioner?: string; backdrop?: string; content?: string; closeTrigger?: string; title?: string; description?: string }
initialFocusEl?() → MaybeElement | false

Element to receive focus when the dialog is opened. Return false to skip moving focus, or null to use the default.

Type
() → MaybeElement | false
Returns
MaybeElement | false
lazyMount?boolean

Whether to enable lazy mounting.

Default
false
modal?boolean

Whether to prevent pointer interaction outside the element and hide all content below it.

Default
true
onEscapeKeyDown?(event: KeyboardEvent) → void

Function called when the escape key is pressed.

Parameters

event – { altKey: boolean; charCode: number; ctrlKey: boolean; code: string; getModifierState: (key: string) → boolean; key: string; keyCode: number; locale: string; location: number; metaKey: boolean; repeat: boolean; shiftKey: boolean; which: number }

Returns
void
onFocusOutside?(event: FocusOutsideEvent) → void

Function called when the focus is moved outside the component.

Type
(event: FocusOutsideEvent) → void
Parameters

event – FocusOutsideEvent

Returns
void
onInteractOutside?(event: InteractOutsideEvent) → void

Function called when an interaction happens outside the component.

Type
(event: InteractOutsideEvent) → void
Parameters

event – InteractOutsideEvent

Returns
void
onPointerDownOutside?(event: PointerDownOutsideEvent) → void

Function called when the pointer is pressed down outside the component.

Type
(event: PointerDownOutsideEvent) → void
Parameters

event – PointerDownOutsideEvent

Returns
void
onRequestDismiss?(event: LayerDismissEvent) → void

Function called when this layer is closed due to a parent layer being closed.

Type
(event: LayerDismissEvent) → void
Parameters

event – LayerDismissEvent

Returns
void
persistentElements?Array<() → Element | null>

Returns the persistent elements that: - should not have pointer-events disabled - should not trigger the dismiss event

Type
Array<() → Element | null>
preventScroll?boolean

Whether to prevent scrolling behind the dialog when it's opened.

Default
true
restoreFocus?boolean

Whether to restore focus to the element that had focus before the dialog was opened.

role?"dialog" | "alertdialog"

The dialog's role.

Type
"dialog" | "alertdialog"
Default
"dialog"
skipAnimationOnMount?boolean

Whether to allow the initial presence animation.

Default
false
trapFocus?boolean

Whether to trap focus inside the dialog when it's opened.

Default
true
unmountOnExit?boolean

Whether to unmount on exit.

Default
false
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>

SheetTrigger

<button> data-slot="sheet-trigger"

PropType
value?string

The value that identifies this specific trigger.

asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
AttributeType
data-current?string

Present when current.

data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"
data-value?string

The value of the item.

SheetOverlay

<div> data-slot="sheet-overlay"

PropType
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

AttributeType
data-has-nested?string

Present when the backdrop has nested dialogs.

data-nested?string

Present when the backdrop is nested within another dialog.

data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"
CSS variableType
--insetstring
--layer-indexstring

The index of the dismissable in the layer stack.

SheetPositioner

<div> data-slot="sheet-positioner"

PropType
placement?SheetSide

Deprecated. Prefer side. Kept for existing callers.

side?SheetSide

Edge of the screen where the sheet appears.

Default
right]:pt-(--inset) data-[side=right]:pb-(--inset)"
variant?"default" | "inset"

The visual style.

Type
"default" | "inset"
Default
"default"
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

AttributeType
data-has-nested?string

Present when the positioner has nested dialogs.

data-nested?string

Present when the positioner is nested within another dialog.

data-placement?string

Mirrors side.

data-side?string

Mirrors side.

data-variant?string

Mirrors variant.

SheetContent

<div> data-slot="sheet-content"

PropType
placement?SheetSide

Deprecated. Prefer side. Kept for Sidebar and other callers.

showCloseButton?boolean

Show close button at the top right corner.

Default
true
side?SheetSide

Edge of the screen where the sheet appears.

Default
"right"
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

AttributeType
data-has-nested?string

Present when another dialog is nested inside it.

data-nested?string

Present when it's nested inside another dialog.

data-side?string

Mirrors side.

data-state"open" | "closed"

"open" or "closed".

Type
"open" | "closed"
CSS variableType
--layer-indexstring

The index of the dismissable in the layer stack.

--nested-layer-countstring

The number of nested dialogs.

SheetTitle

<h2> data-slot="sheet-title"

PropType
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

SheetDescription

<div> data-slot="sheet-description"

PropType
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

SheetClose

<button> data-slot="sheet-close"

PropType
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>

SheetHeader

<div> data-slot="sheet-header"

PropType
description?string

The description of the sheet.

title?string

The title of the sheet.

class?string

Merged with the part's classes.

SheetBody

data-slot="sheet-body"

PropType
scrollFade?boolean

Add a fade effect to the scroll area.

Default
false
asChild?Snippet<[PropsFn]>

Render your own element: a snippet that gets the part's props to spread on it.

Type
Snippet<[PropsFn]>
class?string

Merged with the part's classes.

SheetFooter

data-slot="sheet-footer"

PropType
class?string

Merged with the part's classes.

useSheet

With other components