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
The block is the drawer panel; drawer.trigger marks
what opens it — the same shape as sui.dialog. Child
parts (drawer.header, …) live on the proxy.
Drawer
Side panel content goes here.
<%= sui.drawer do |drawer| %> <%= drawer.trigger { sui.button "Open drawer", variant: :outline } %> <%= drawer.header do %> <%= drawer.title "Drawer" %> <%= drawer.description "Side panel content goes here." %> <% end %> <div class="p-4 text-sm">Body content.</div> <% end %>
Sides
Pass side: to sui.drawer — one of :right (default), :left, :top, :bottom.
Right drawer
Slides in from the right.
Left drawer
Slides in from the left.
Top drawer
Slides down from the top.
Bottom drawer
Slides up from the bottom.
<div class="flex flex-wrap gap-2"> <%= sui.drawer do |drawer| %> <%= drawer.trigger { sui.button "Right (default)", variant: :outline } %> <%= 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 %> <%= sui.drawer side: :left do |drawer| %> <%= drawer.trigger { sui.button "Left", variant: :outline } %> <%= 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 %> <%= sui.drawer side: :top do |drawer| %> <%= drawer.trigger { sui.button "Top", variant: :outline } %> <%= 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 %> <%= sui.drawer side: :bottom do |drawer| %> <%= drawer.trigger { sui.button "Bottom", variant: :outline } %> <%= 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 %> </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 { sui.button "Settings", variant: :outline } %> <%= 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 %>
Lazy loading and triggers
Drawer takes the same lazy-loading and trigger options as
sui.dialog: src: /
reload: / loading: fetch the panel
via a Turbo Frame, the drawer: kwarg on
sui.button takes a Hash to wrap the button or a
String id to open a drawer from anywhere in the page.
Loading...
<%= sui.button "Lazy drawer", variant: :outline, drawer: { src: lazy_hover_card_content_path, loading: sui.skeleton(class: "h-24") } %> <%= sui.button "Detached trigger", variant: :outline, drawer: "detached-demo-drawer" %> <%= sui.drawer src: lazy_hover_card_content_path, side: :left, id: "detached-demo-drawer" %>
API
| Method | Args | Default | Description |
|---|---|---|---|
sui.drawer | side:, src:, reload:, loading:, content:, **opts | :right | Wrapper; the block body is the panel (backdrop + edge-anchored sheet). side: is :right :left :top :bottom; content: is the panel's option hash. src: lazy-loads the panel via Turbo Frame (body or loading: = loading state) |
drawer.trigger | &block | — | Marks which part of the block is the trigger; your markup, verbatim — click-to-open is bound on the wrapper. Mark one element data-slot="drawer-trigger" to scope it in composite triggers |
drawer.header | **opts | — | Padded flex column for title and description |
drawer.footer | **opts | — | Sticky bottom area with padding for action buttons |
drawer.title | name | nil | Semibold heading rendered as h2 |
drawer.description | name | nil | Muted supporting text rendered as p |