Drawer
A slide-out panel that can appear from any edge of the screen. Perfect for mobile-optimized navigation, forms, and detail views.
Edit profile
Make changes to your profile here. Click save when you're done.
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
Installation
Add the component to your project:
rails generate shadcn:add drawer
Usage
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
Examples
Basic Drawer
Are you sure?
This action cannot be undone.
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
With Form
Drawers are perfect for forms and data entry on mobile devices.
Edit Profile
Make changes to your profile here.
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new do %>Edit Profile<% end %>
<% end %>
<% drawer.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="p-4 space-y-4">
<div class="space-y-2">
<%= render Shadcn::LabelComponent.new(for: "form-name") { "Name" } %>
<%= render Shadcn::InputComponent.new(id: "form-name", value: "John Doe") %>
</div>
<div class="space-y-2">
<%= render Shadcn::LabelComponent.new(for: "form-username") { "Username" } %>
<%= render Shadcn::InputComponent.new(id: "form-username", value: "@johndoe") %>
</div>
</div>
<% content.with_footer do %>
<%= render Shadcn::ButtonComponent.new(type: "submit", class_name: "w-full") { "Save changes" } %>
<% end %>
<% end %>
<% end %>
Different Directions
Drawers can slide in from any edge: bottom (default), top, left, or right.
Bottom Drawer
Content slides in from the bottom.
Drawer content here.
Top Drawer
Content slides in from the top.
Drawer content here.
Left Drawer
Content slides in from the left.
Drawer content here.
Right Drawer
Content slides in from the right.
Drawer content here.
<div class="flex flex-wrap gap-2">
<%= render Shadcn::DrawerComponent.new(direction: :bottom) do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Bottom<% end %>
<% end %>
<% drawer.with_body(direction: :bottom) do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Bottom Drawer" } %>
<% header.with_description { "Content slides in from the bottom." } %>
<% end %>
<div class="p-4">
<p>Drawer content here.</p>
</div>
<% end %>
<% end %>
<%= render Shadcn::DrawerComponent.new(direction: :top) do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Top<% end %>
<% end %>
<% drawer.with_body(direction: :top) do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Top Drawer" } %>
<% header.with_description { "Content slides in from the top." } %>
<% end %>
<div class="p-4">
<p>Drawer content here.</p>
</div>
<% end %>
<% end %>
<%= render Shadcn::DrawerComponent.new(direction: :left) do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Left<% end %>
<% end %>
<% drawer.with_body(direction: :left) do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Left Drawer" } %>
<% header.with_description { "Content slides in from the left." } %>
<% end %>
<div class="p-4 flex-1">
<p>Drawer content here.</p>
</div>
<% end %>
<% end %>
<%= render Shadcn::DrawerComponent.new(direction: :right) do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Right<% end %>
<% end %>
<% drawer.with_body(direction: :right) do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Right Drawer" } %>
<% header.with_description { "Content slides in from the right." } %>
<% end %>
<div class="p-4 flex-1">
<p>Drawer content here.</p>
</div>
<% end %>
<% end %>
</div>
From Left
From Right
From Top
Custom Close Action
Use data-action="click->shadcn--drawer#close" on any element to close the drawer.
Right Side
Shopping Cart
Review your items before checkout.
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
Left Side
Navigation
Browse application sections.
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
Top
Notifications
You have 3 unread messages.
New comment
John Doe commented on your post
New follower
Alice Brown started following you
<%= render Shadcn::DrawerComponent.new do |drawer| %>
<% drawer.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) do %>Basic Drawer<% end %>
<% end %>
<% drawer.with_body do |content| %>
<% content.with_header do |header| %>
<% header.with_title { "Are you sure?" } %>
<% header.with_description { "This action cannot be undone." } %>
<% end %>
<% content.with_footer do %>
<div class="flex gap-2 justify-end">
<%= render Shadcn::ButtonComponent.new(variant: :outline, data: { action: "click->shadcn--drawer#close" }) { "Cancel" } %>
<%= render Shadcn::ButtonComponent.new(variant: :destructive) { "Delete" } %>
</div>
<% end %>
<% end %>
<% end %>
API Reference
DrawerComponent
| Prop | Type | Default | Description |
|---|---|---|---|
| open |
Boolean
|
false
|
Whether the drawer starts in open state |
| direction |
Symbol
|
:bottom
|
Which edge the drawer slides from (:bottom, :top, :left, :right) |
Slots
| Slot | Description |
|---|---|
| with_trigger | The element that opens the drawer (usually a button) |
| with_body | The drawer content container (yields DrawerContentComponent) |
DrawerContentComponent Slots
| Slot | Description |
|---|---|
| with_header | Header section (yields with_title and with_description slots) |
| with_title | Drawer title text (via header) |
| with_description | Drawer description text (via header) |
| with_footer | Footer section (usually contains action buttons) |
Note: Mobile gesture support (swipe to close) is planned for a future release.
Stimulus Controller
This component requires JavaScript. The Stimulus controller shadcn--drawer provides interactivity.
Manages drawer open/close state, portal rendering, keyboard navigation, and animations.
Installation
Add to your config/importmap.rb:
pin "shadcn", to: "index.esm.js"
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
yarn add shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Or import just this controller:
import DrawerController from "shadcn-rails-stimulus/controllers/drawer_controller"
application.register("shadcn--drawer", DrawerController)
Targets
| Name | Description |
|---|---|
| trigger | The element that triggers the drawer to open |
| template | Template containing the drawer content (for portal rendering) |
| overlay | The backdrop overlay element |
| content | The drawer content container |
Values
| Name | Type | Default | Description |
|---|---|---|---|
| open | Boolean |
false
|
Current open/closed state |
| direction | String |
bottom
|
Which edge the drawer slides from |
Actions
| Action | Description |
|---|---|
| open | Opens the drawer |
| close | Closes the drawer |
| toggle | Toggles the drawer open/closed state |
TypeScript
Type definitions are included. Import types as needed:
import type { DrawerController } from "shadcn-rails-stimulus"
Accessibility
- Uses
role="dialog"for proper screen reader announcement - Sets
aria-modal="true"to indicate modal behavior - Focuses the drawer content when opened
- Pressing Escape closes the drawer
- Prevents body scroll when drawer is open
- Handle bar is shown for bottom/top drawers to indicate draggable behavior (visual only)
- Clicking the overlay backdrop closes the drawer
On This Page