Dialog

A modal dialog that interrupts the user with important content and expects a response.

Requires JavaScript
dialog/default
<%= 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/default
<%= 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

dialog/alert
<%= 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

dialog/with_form
<%= 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)

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-labelledby and aria-describedby