Blocks

Drawer

A slide-in panel anchored to any edge of the viewport — supports right, left, top, and bottom sides with animated open/close transitions.

Installation

bin/rails g shadcnrb:component drawer

Usage

Wrap everything in sui.drawer do |drawer|. Child parts (drawer.trigger, drawer.content, …) live on the proxy.

Drawer

Side panel content goes here.

Body content.
<%= sui.drawer do |drawer| %>
  <%= drawer.trigger "Open drawer", variant: :outline %>
  <%= drawer.content do %>
    <%= drawer.header do %>
      <%= drawer.title "Drawer" %>
      <%= drawer.description "Side panel content goes here." %>
    <% end %>
    <div class="p-4 text-sm">Body content.</div>
  <% end %>
<% end %>

Sides

Pass side: to drawer.content — one of :right (default), :left, :top, :bottom.

Right drawer

Slides in from the right.

Content panel.

Left drawer

Slides in from the left.

Content panel.

Top drawer

Slides down from the top.

Content panel.

Bottom drawer

Slides up from the bottom.

Content panel.
<div class="flex flex-wrap gap-2">
  <%= sui.drawer do |drawer| %>
    <%= drawer.trigger "Right (default)", variant: :outline %>
    <%= drawer.content do %>
      <%= drawer.header do %>
        <%= drawer.title "Right drawer" %>
        <%= drawer.description "Slides in from the right." %>
      <% end %>
      <div class="p-4 text-sm">Content panel.</div>
    <% end %>
  <% end %>

  <%= sui.drawer do |drawer| %>
    <%= drawer.trigger "Left", variant: :outline %>
    <%= drawer.content side: :left do %>
      <%= drawer.header do %>
        <%= drawer.title "Left drawer" %>
        <%= drawer.description "Slides in from the left." %>
      <% end %>
      <div class="p-4 text-sm">Content panel.</div>
    <% end %>
  <% end %>

  <%= sui.drawer do |drawer| %>
    <%= drawer.trigger "Top", variant: :outline %>
    <%= drawer.content side: :top do %>
      <%= drawer.header do %>
        <%= drawer.title "Top drawer" %>
        <%= drawer.description "Slides down from the top." %>
      <% end %>
      <div class="p-4 text-sm">Content panel.</div>
    <% end %>
  <% end %>

  <%= sui.drawer do |drawer| %>
    <%= drawer.trigger "Bottom", variant: :outline %>
    <%= drawer.content side: :bottom do %>
      <%= drawer.header do %>
        <%= drawer.title "Bottom drawer" %>
        <%= drawer.description "Slides up from the bottom." %>
      <% end %>
      <div class="p-4 text-sm">Content panel.</div>
    <% end %>
  <% end %>
</div>

Settings drawer

A practical example with a header, scrollable body, and sticky footer.

Settings

Manage your account preferences.

<%= sui.drawer do |drawer| %>
  <%= drawer.trigger "Settings", variant: :outline %>
  <%= drawer.content do %>
    <%= drawer.header do %>
      <%= drawer.title "Settings" %>
      <%= drawer.description "Manage your account preferences." %>
    <% end %>
    <div class="flex-1 overflow-y-auto p-4 space-y-4">
      <div class="flex items-center justify-between">
        <%= sui.label "Dark mode" %>
        <%= sui.switch name: "dark_mode" %>
      </div>
      <div class="flex items-center justify-between">
        <%= sui.label "Notifications" %>
        <%= sui.switch name: "notifications", checked: true %>
      </div>
    </div>
    <%= drawer.footer do %>
      <%= sui.button "Save changes" %>
    <% end %>
  <% end %>
<% end %>

API

MethodArgsDefaultDescription
sui.drawer**optsRoot container; mounts the Stimulus controller and yields a drawer proxy
drawer.triggername, variant:, size::default, :mdButton that opens the drawer panel on click
drawer.contentside::rightBackdrop + anchored panel; side: is :right :left :top :bottom
drawer.header**optsPadded flex column for title and description
drawer.footer**optsSticky bottom area with padding for action buttons
drawer.titlenamenilSemibold heading rendered as h2
drawer.descriptionnamenilMuted supporting text rendered as p