Sheet

A panel that slides out from the edge of the screen. Extends the Dialog component to display content without covering the entire screen.

Requires JavaScript

Installation

Add the component to your project:

rails generate shadcn:component sheet

Usage

<%= render Shadcn::SheetComponent.new(side: :right) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Open Sheet<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Edit Profile" } %>
      <% header.with_description { "Make changes to your profile here." } %>
    <% end %>
    <div class="grid gap-4 py-4">
      <div class="grid grid-cols-4 items-center gap-4">
        <%= render Shadcn::LabelComponent.new(for: "name", class_name: "text-right") { "Name" } %>
        <%= render Shadcn::InputComponent.new(id: "name", value: "John Doe", class_name: "col-span-3") %>
      </div>
      <div class="grid grid-cols-4 items-center gap-4">
        <%= render Shadcn::LabelComponent.new(for: "username", class_name: "text-right") { "Username" } %>
        <%= render Shadcn::InputComponent.new(id: "username", value: "@johndoe", class_name: "col-span-3") %>
      </div>
    </div>
    <% content.with_footer do %>
      <%= render Shadcn::ButtonComponent.new(type: "submit") { "Save changes" } %>
    <% end %>
  <% end %>
<% end %>

Examples

Sides

Use the side property to specify which edge the sheet slides out from. Options: :top, :right, :bottom, :left.

Top

<%= render Shadcn::SheetComponent.new(side: :top) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Top<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Notifications" } %>
      <% header.with_description { "View your recent notifications." } %>
    <% end %>
    <div class="py-4">
      <p class="text-sm">No new notifications.</p>
    </div>
  <% end %>
<% end %>

Right (Default)

<%= render Shadcn::SheetComponent.new(side: :right) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Right<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Settings" } %>
      <% header.with_description { "Configure your preferences." } %>
    <% end %>
    <div class="py-4">
      <p class="text-sm">Settings panel content.</p>
    </div>
  <% end %>
<% end %>

Bottom

<%= render Shadcn::SheetComponent.new(side: :bottom) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Bottom<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Details" } %>
      <% header.with_description { "Additional information." } %>
    <% end %>
    <div class="py-4">
      <p class="text-sm">Details panel content.</p>
    </div>
  <% end %>
<% end %>

Left

<%= render Shadcn::SheetComponent.new(side: :left) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Left<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Navigation" } %>
      <% header.with_description { "Browse sections." } %>
    <% end %>
    <div class="py-4">
      <nav class="flex flex-col gap-2">
        <a href="#" class="text-sm hover:underline">Home</a>
        <a href="#" class="text-sm hover:underline">About</a>
        <a href="#" class="text-sm hover:underline">Contact</a>
      </nav>
    </div>
  <% end %>
<% end %>

With Form

Sheets are ideal for forms and multi-step workflows that don't need to completely interrupt the user.

<%= render Shadcn::SheetComponent.new(side: :right) do |sheet| %>
  <% sheet.with_trigger do %>
    <%= render Shadcn::ButtonComponent.new do %>Create Account<% end %>
  <% end %>
  <% sheet.with_body do |content| %>
    <% content.with_header do |header| %>
      <% header.with_title { "Create New Account" } %>
      <% header.with_description { "Enter the details below to create a new account." } %>
    <% end %>
    <div class="grid gap-4 py-4">
      <div class="space-y-2">
        <%= render Shadcn::LabelComponent.new(for: "name") { "Full Name" } %>
        <%= render Shadcn::InputComponent.new(id: "name", placeholder: "John Doe") %>
      </div>
      <div class="space-y-2">
        <%= render Shadcn::LabelComponent.new(for: "email") { "Email" } %>
        <%= render Shadcn::InputComponent.new(id: "email", type: "email", placeholder: "email@example.com") %>
      </div>
      <div class="space-y-2">
        <%= render Shadcn::LabelComponent.new(for: "role") { "Role" } %>
        <%= render Shadcn::InputComponent.new(id: "role", placeholder: "Developer") %>
      </div>
    </div>
    <% content.with_footer do %>
      <div class="flex gap-2">
        <%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--sheet#close" }) { "Cancel" } %>
        <%= render Shadcn::ButtonComponent.new(type: "submit") { "Create Account" } %>
      </div>
    <% end %>
  <% end %>
<% end %>

API Reference

SheetComponent

API Reference

Prop Type Default Description
side Symbol :right Side the sheet appears from. Options: :top, :right, :bottom, :left
open Boolean false Whether the sheet starts in open state
class_name String nil Additional CSS classes to apply
data Hash {} Data attributes to add to the element

Slots

Slot Description
with_trigger The element that opens the sheet (usually a button)
with_body The sheet content container (yields SheetContentComponent)

SheetContentComponent Slots

Slot Description
with_header Header section (yields with_title and with_description slots)
with_title Sheet title text
with_description Sheet description text
with_footer Footer section (usually contains action buttons)

Stimulus Controller

This component requires JavaScript. The Stimulus controller shadcn--sheet provides interactivity.

Manages sheet open/close state, focus trapping, keyboard navigation, and accessibility for slide-out panels.

Installation

Add to your config/importmap.rb:

pin "shadcn-rails", to: "shadcn/index.js"

Then in your app/javascript/controllers/index.js:

import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"

const application = Application.start()
registerShadcnControllers(application)

Or import just this controller:

import SheetController from "shadcn-rails/controllers/sheet_controller"

application.register("shadcn--sheet", SheetController)

Targets

Name Description
trigger The element that triggers the sheet to open
template Template containing the sheet content (for portal rendering)
overlay The backdrop overlay element
content The sheet panel container

Values

Name Type Default Description
open Boolean false Current open/closed state
side String right Which side the sheet appears from

Actions

Action Description
open Opens the sheet
close Closes the sheet

TypeScript

Type definitions are included. Import types as needed:

import type { SheetController } from "shadcn-rails"

Accessibility

  • Uses role="dialog" for proper screen reader announcement
  • Sets aria-modal="true" to indicate modal behavior
  • Traps focus within the sheet when open
  • Pressing Escape closes the sheet
  • Returns focus to trigger element when closed
  • Clicking the overlay closes the sheet
  • Use data-action="click->shadcn--sheet#close" on any element to close the sheet programmatically