Files
leptos-shadcn-ui/docs/components/api/dialog.md
Ubuntu 18c4ddcd05 drover: task-1767764907653950146
Task: Improve API documentation
2026-01-10 05:38:15 +00:00

12 KiB

Dialog Component API

A modal dialog component for displaying important information or capturing user input in an overlay.


Installation

# Cargo.toml
[dependencies]
shadcn-ui-leptos-dialog = "0.7"
use shadcn_ui_leptos_dialog::Dialog;

Import

// Default theme
use shadcn_ui_leptos_dialog::{
    Dialog,
    DialogTrigger,
    DialogContent,
    DialogHeader,
    DialogTitle,
    DialogDescription,
    DialogFooter,
    DialogClose
};

// New York theme
use shadcn_ui_leptos_dialog::{
    Dialog as DialogNewYork,
    DialogTrigger as DialogTriggerNewYork,
    // ... other components
};

// Signal-managed variant
use shadcn_ui_leptos_dialog::{
    SignalManagedDialog,
    SignalManagedDialogState
};

Component API

Dialog (Root)

The context provider that manages dialog state.

Prop Type Default Description
open Signal<bool> Required Controls dialog visibility
on_open_change Option<Callback<bool>> None Called when open state changes
children Option<Children> None Dialog content

DialogTrigger

Button that opens the dialog.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
id MaybeProp<String> None Unique identifier
style Signal<Style> None Inline styles
node_ref AnyNodeRef None Node reference
as_child Option<Callback<DialogTriggerChildProps, AnyView>> None Render as custom element
children Option<Children> None Trigger content

DialogContent

The main dialog container with backdrop.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
style Signal<Style> None Inline styles
children Option<Children> None Dialog content

DialogHeader

Header section for title and description.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
children Option<Children> None Header content

DialogTitle

Dialog title (accessibility required).

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
children Option<Children> None Title text

DialogDescription

Optional description text.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
children Option<Children> None Description text

DialogFooter

Footer section for action buttons.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
children Option<Children> None Footer content

DialogClose

Button that closes the dialog.

Prop Type Default Description
class MaybeProp<String> None Additional CSS classes
children Option<Children> None Button content

Usage Examples

Basic Dialog

use leptos::prelude::*;
use shadcn_ui_leptos_dialog::*;

#[component]
pub fn MyComponent() -> impl IntoView {
    let (open, set_open) = signal(false);

    view! {
        <Dialog open=open.into() on_open_change=Some(Callback::new(move |is_open| {
            set_open.set(is_open);
        }))>
            <DialogTrigger>
                <button>"Open Dialog"</button>
            </DialogTrigger>
            <DialogContent>
                <DialogHeader>
                    <DialogTitle>"Are you sure?"</DialogTitle>
                </DialogHeader>
                <div class="py-4">
                    "This action cannot be undone."
                </div>
                <DialogFooter>
                    <DialogClose>
                        <button>"Cancel"</button>
                    </DialogClose>
                    <button class="bg-destructive">"Confirm"</button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    }
}

Custom Trigger Button

view! {
    <Dialog open=open.into()>
        <DialogTrigger as_child=Callback::new(|props| {
            view! {
                <button
                    class=props.class
                    on:click=move |_| set_open.set(true)
                >
                    "Open"
                </button>
            }.into_any()
        })>
        </DialogTrigger>
        // ... rest of dialog
    </Dialog>
}

With Description

view! {
    <DialogContent>
        <DialogHeader>
            <DialogTitle>"Delete Account"</DialogTitle>
            <DialogDescription>
                "This will permanently delete your account and all associated data. \
                This action cannot be undone."
            </DialogDescription>
        </DialogHeader>
        // ... actions
    </DialogContent>
}

Form Dialog

#[component]
pub fn CreateUserDialog() -> impl IntoView {
    let (open, set_open) = signal(false);
    let (name, set_name) = signal(String::new());
    let (email, set_email) = signal(String::new());

    let handle_submit = move |_| {
        // Create user...
        set_open.set(false);
    };

    view! {
        <Dialog open=open.into()>
            <DialogTrigger>
                <button>"Add User"</button>
            </DialogTrigger>
            <DialogContent>
                <DialogHeader>
                    <DialogTitle>"Create New User"</DialogTitle>
                    <DialogDescription>
                        "Enter the user's information below"
                    </DialogDescription>
                </DialogHeader>
                <form on:submit=handle_submit class="space-y-4">
                    <div>
                        <label>"Name"</label>
                        <input
                            type="text"
                            on:input=move |e| set_name.set(event_target_value(&e))
                        />
                    </div>
                    <div>
                        <label>"Email"</label>
                        <input
                            type="email"
                            on:input=move |e| set_email.set(event_target_value(&e))
                        />
                    </div>
                    <DialogFooter>
                        <DialogClose>
                            <button type="button">"Cancel"</button>
                        </DialogClose>
                        <button type="submit">"Create"</button>
                    </DialogFooter>
                </form>
            </DialogContent>
        </Dialog>
    }
}

Confirmation Dialog

#[component]
pub fn ConfirmDelete() -> impl IntoView {
    let (open, set_open) = signal(false);

    view! {
        <Dialog open=open.into()>
            <DialogTrigger>
                <button class="text-destructive">"Delete"</button>
            </DialogTrigger>
            <DialogContent>
                <DialogHeader>
                    <DialogTitle>"Delete Item"</DialogTitle>
                    <DialogDescription>
                        "Are you sure you want to delete this item? \
                        This action cannot be undone."
                    </DialogDescription>
                </DialogHeader>
                <DialogFooter>
                    <DialogClose>
                        <button variant="outline">"Cancel"</button>
                    </DialogClose>
                    <button
                        class="bg-destructive"
                        on:click=move |_| {
                            // Delete logic...
                            set_open.set(false);
                        }
                    >
                        "Delete"
                    </button>
                </DialogFooter>
            </DialogContent>
        </Dialog>
    }
}

Custom Styling

view! {
    <DialogContent class="sm:max-w-md">
        <DialogHeader>
            <DialogTitle class="text-lg font-semibold">
                "Custom Styled Dialog"
            </DialogTitle>
        </DialogHeader>
        <div class="mt-4">
            "Content with custom styling"
        </div>
    </DialogContent>
}

Accessibility

Keyboard Navigation

Key Action
Escape Close dialog
Tab Navigate focus within dialog
Shift+Tab Navigate backwards

ARIA Attributes

The component automatically manages:

  • role="dialog" - Dialog role
  • aria-modal="true" - Modal state
  • aria-labelledby - References title element
  • aria-describedby - References description element

Focus Management

  1. Focus trap - Tab cycles within dialog
  2. Initial focus - First focusable element
  3. Return focus - Focus returns to trigger on close
  4. Backdrop click - Click outside closes dialog

Screen Reader Support

// Good: Proper title and description
<DialogContent>
    <DialogHeader>
        <DialogTitle>"Delete Account"</DialogTitle>
        <DialogDescription>
            "This will permanently delete your account"
        </DialogDescription>
    </DialogHeader>
</DialogContent>

// Good: Descriptive trigger
<DialogTrigger>
    <button>"Delete Account"</button>
</DialogTrigger>

CSS Classes

Content Classes

.dialog-content {
    fixed inset-0 z-50 flex items-center justify-center
    bg-background/80 backdrop-blur-sm
    data-[state=open]:animate-in data-[state=closed]:animate-out
    data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
    data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95
    data-[state=closed]:slide-out-to-left-1/2
    data-[state=closed]:slide-out-to-top-[48%]
    data-[state=open]:slide-in-from-left-1/2
    data-[state=open]:slide-in-from-top-[48%]
    sm:rounded-lg
}

Header Classes

.dialog-header {
    flex flex-col space-y-1.5 text-center sm:text-left
}

Title Classes

.dialog-title {
    text-lg font-semibold leading-none tracking-tight
}

Description Classes

.dialog-description {
    text-sm text-muted-foreground
}
.dialog-footer {
    flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2
}

TypeScript API

interface DialogProps {
  open: boolean;
  onOpenChange?: (open: boolean) => void;
  children?: React.ReactNode;
}

interface DialogContentProps {
  className?: string;
  children?: React.ReactNode;
}

interface DialogHeaderProps {
  className?: string;
  children?: React.ReactNode;
}

interface DialogTitleProps {
  className?: string;
  children?: React.ReactNode;
}

interface DialogDescriptionProps {
  className?: string;
  children?: React.ReactNode;
}

interface DialogFooterProps {
  className?: string;
  children?: React.ReactNode;
}

export const Dialog: React.FC<DialogProps>;
export const DialogTrigger: React.FC<ButtonProps>;
export const DialogContent: React.FC<DialogContentProps>;
export const DialogHeader: React.FC<DialogHeaderProps>;
export const DialogTitle: React.FC<DialogTitleProps>;
export const DialogDescription: React.FC<DialogDescriptionProps>;
export const DialogFooter: React.FC<DialogFooterProps>;
export const DialogClose: React.FC<ButtonProps>;

Best Practices

  1. Always provide a title - Required for accessibility
  2. Use descriptions for context - Explain the dialog's purpose
  3. Provide clear actions - Make cancel/confirm obvious
  4. Handle escape key - Built-in, but test your implementation
  5. Return focus properly - Ensures good UX for keyboard users

See Also


Source: packages/leptos/dialog/src/default.rs