Dialog
A modal dialog that interrupts the user with important content and expects a response.
Dialog Title
This is a description of what this dialog does.
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Dialog" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Dialog Title" } %>
<% header.with_description { "This is a description of what this dialog does." } %>
<% end %>
<div class="py-4">
<p class="text-sm text-muted-foreground">Your dialog content goes here. You can put any content inside a dialog.</p>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new { "Continue" } %>
<% end %>
<% end %>
<% end %>
Installation
Add the component to your project:
rails generate shadcn:add dialog
Usage
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Dialog" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Dialog Title" } %>
<% header.with_description { "This is a description of what this dialog does." } %>
<% end %>
<div class="py-4">
<p class="text-sm text-muted-foreground">Your dialog content goes here. You can put any content inside a dialog.</p>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new { "Continue" } %>
<% end %>
<% end %>
<% end %>
Examples
Basic Dialog
Dialog Title
This is a description of what this dialog does.
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Dialog" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Dialog Title" } %>
<% header.with_description { "This is a description of what this dialog does." } %>
<% end %>
<div class="py-4">
<p class="text-sm text-muted-foreground">Your dialog content goes here. You can put any content inside a dialog.</p>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new { "Continue" } %>
<% end %>
<% end %>
<% end %>
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Dialog" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Dialog Title" } %>
<% header.with_description { "This is a description of what this dialog does." } %>
<% end %>
<div class="py-4">
<p class="text-sm text-muted-foreground">Your dialog content goes here. You can put any content inside a dialog.</p>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new { "Continue" } %>
<% end %>
<% end %>
<% end %>
Alert Dialog Style
Are you absolutely sure?
This action cannot be undone. This will permanently delete your account and remove your data from our servers.
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :destructive) { "Delete Account" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Are you absolutely sure?" } %>
<% header.with_description { "This action cannot be undone. This will permanently delete your account and remove your data from our servers." } %>
<% end %>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new(variant: :destructive) { "Delete Account" } %>
<% end %>
<% end %>
<% end %>
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :destructive) { "Delete Account" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Are you absolutely sure?" } %>
<% header.with_description { "This action cannot be undone. This will permanently delete your account and remove your data from our servers." } %>
<% end %>
<% body.with_footer do %>
<%= render Shadcn::Button.new(variant: :outline, class_name: "mr-2") { "Cancel" } %>
<%= render Shadcn::Button.new(variant: :destructive) { "Delete Account" } %>
<% end %>
<% end %>
<% end %>
With Form
Edit profile
Make changes to your profile here. Click save when you're done.
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Edit Profile" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Edit profile" } %>
<% header.with_description { "Make changes to your profile here. Click save when you're done." } %>
<% end %>
<div class="grid gap-4 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<label for="name" class="text-right text-sm font-medium leading-none">Name</label>
<input type="text" id="name" value="Pedro Duarte" class="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<label for="username" class="text-right text-sm font-medium leading-none">Username</label>
<input type="text" id="username" value="@peduarte" class="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
</div>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new { "Save changes" } %>
<% end %>
<% end %>
<% end %>
<%= render Shadcn::Dialog.new do |dialog| %>
<% dialog.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Edit Profile" } %>
<% end %>
<% dialog.with_body do |body| %>
<% body.with_header do |header| %>
<% header.with_title { "Edit profile" } %>
<% header.with_description { "Make changes to your profile here. Click save when you're done." } %>
<% end %>
<div class="grid gap-4 py-4">
<div class="grid grid-cols-4 items-center gap-4">
<label for="name" class="text-right text-sm font-medium leading-none">Name</label>
<input type="text" id="name" value="Pedro Duarte" class="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<label for="username" class="text-right text-sm font-medium leading-none">Username</label>
<input type="text" id="username" value="@peduarte" class="col-span-3 flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm" />
</div>
</div>
<% body.with_footer do %>
<%= render Shadcn::Button.new { "Save changes" } %>
<% end %>
<% end %>
<% end %>
API Reference
DialogComponent
| Prop | Type | Default | Description |
|---|---|---|---|
| id |
String
|
nil
|
Unique identifier for the dialog (useful for Turbo Stream targeting) |
| open |
Boolean
|
false
|
Whether the dialog starts in open state |
| modal |
Boolean
|
true
|
Whether dialog is modal (traps focus, blocks background interaction) |
Slots
| Slot | Description |
|---|---|
| with_trigger | The element that opens the dialog (usually a button) |
| with_body | The dialog content container (yields DialogContentComponent) |
DialogContentComponent Slots
| Slot | Description |
|---|---|
| with_header | Header section (yields with_title and with_description slots) |
| with_title | Dialog title text |
| with_description | Dialog description text |
| with_footer | Footer section (usually contains action buttons) |
| with_close_button | Custom close button element |
Stimulus Controller
This component requires JavaScript. The Stimulus controller shadcn--dialog provides interactivity.
Manages dialog open/close state, focus trapping, keyboard navigation, and accessibility.
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 DialogController from "shadcn-rails-stimulus/controllers/dialog_controller"
application.register("shadcn--dialog", DialogController)
Targets
| Name | Description |
|---|---|
| trigger | The element that triggers the dialog to open |
| template | Template containing the dialog content (for portal rendering) |
| overlay | The backdrop overlay element |
| content | The dialog content container |
Values
| Name | Type | Default | Description |
|---|---|---|---|
| open | Boolean |
false
|
Current open/closed state |
| modal | Boolean |
true
|
Whether dialog is modal |
Actions
| Action | Description |
|---|---|
| open | Opens the dialog |
| close | Closes the dialog |
TypeScript
Type definitions are included. Import types as needed:
import type { DialogController } from "shadcn-rails-stimulus"
Accessibility
- Uses
role="dialog"for proper screen reader announcement - Sets
aria-modal="true"when modal is enabled - Traps focus within the dialog when open
- Pressing Escape closes the dialog
- Returns focus to trigger element when closed
- Links title and description via
aria-labelledbyandaria-describedby
On This Page