Dialog
Dialog component for creating dialogs, confirmations, and alerts. Supports overlay, keyboard shortcuts, and various customizations.
#Import
use gpui_kit::component::dialog::DialogButtonProps;
use gpui_kit::component::WindowExt;
#Usage
#Where dialogs render
The window’s Root automatically mounts and renders dialogs. Open the window with gpui_kit::open_window, or wrap the application view in Root::new. Application views do not render overlay layers themselves.
#Basic Dialog
window.open_dialog(cx, |dialog, _, _| {
dialog
.title("Welcome")
.child("This is a dialog dialog.")
})
#Builder lifetime and initial focus
The open_dialog builder may run again when the dialog layer is rebuilt; it
is not a one-shot constructor or a callback guaranteed to run on every displayed
frame. The same contract applies to open_alert_dialog, open_sheet, and
open_sheet_at. Keep builders cheap and idempotent. Create persistent entities
before opening and capture their handles, so rebuilding preserves editing state.
open_dialog initially focuses the dialog shell. To focus its content, use a
one-time guard inside the builder, created separately for each opening.
Unconditionally focusing on every rebuild steals focus from other controls or
nested dialogs.
use std::cell::Cell;
use gpui_kit::{AppContext as _, Focusable as _};
use gpui_kit::component::{WindowExt as _, input::{Input, InputState}};
let input = cx.new(|cx| InputState::new(window, cx).placeholder("URL"));
let initial_focus = Cell::new(true);
window.open_dialog(cx, move |dialog, window, cx| {
if initial_focus.replace(false) {
input.focus_handle(cx).focus(window, cx);
}
dialog.title("Import").child(Input::new(&input))
});
#Form Dialog
let input = cx.new(|cx| InputState::new(window, cx));
window.open_dialog(cx, |dialog, _, _| {
dialog
.title("User Information")
.child(
v_flex()
.gap_3()
.child("Please enter your details:")
.child(Input::new(&input))
)
.footer(|_, _, _, _| {
vec![
Button::new("ok")
.primary()
.label("Submit")
.on_click(|_, window, cx| {
window.close_dialog(cx);
}),
Button::new("cancel")
.label("Cancel")
.on_click(|_, window, cx| {
window.close_dialog(cx);
}),
]
})
})
#Dialog with Icon
window.open_dialog(cx, |dialog, _, cx| {
dialog
.child(
h_flex()
.gap_3()
.child(Icon::new(IconName::TriangleAlert)
.size_6()
.text_color(cx.theme().warning))
.child("This action cannot be undone.")
)
})
#Scrollable Dialog
use gpui_kit::component::text::markdown;
window.open_dialog(cx, |dialog, window, cx| {
dialog
.h(px(450.))
.title("Long Content")
.child(markdown(long_markdown_text))
})
A dialog never extends past the window. Its width is capped at the viewport
minus a 16px margin on each side, and its height at the space between its top
offset and a 16px bottom margin, so the title and footer stay visible while the
body scrolls. w, max_w, h, and margin_top apply within those limits;
a dialog that already fits keeps its requested size and default position.
#Dialog Options
window.open_dialog(cx, |dialog, _, _| {
dialog
.title("Custom Dialog")
.overlay(true) // Show overlay (default: true)
.overlay_closable(true) // Click overlay to close (default: true)
.keyboard(true) // ESC to close (default: true)
.close_button(false) // Show close button (default: true)
.child("Dialog content")
})
#Entrance animation
Dialog and AlertDialog share DialogEntrance. The default SlideDown
preserves the existing slide from the window top. Fade fades at the final
position; FadeSlide fades while moving down by the theme’s short motion
distance; None immediately displays both the surface and backdrop.
use gpui_kit::component::dialog::DialogEntrance;
window.open_dialog(cx, |dialog, _, _| {
dialog.title("Settings").entrance(DialogEntrance::FadeSlide)
});
Fade and FadeSlide use the theme’s normal duration and enter easing.
FadeSlide applies travel after resolving window boundaries and limits it to
available space above the surface. Reduced motion displays every entrance at
rest immediately. Closing remains immediate for every option.
#Action Buttons
A Dialog puts its own buttons in the footer and has them
dispatch Confirm or Cancel; on_ok and on_cancel decide what Enter and
Esc do. For a confirmation with default buttons, use
AlertDialog.
#Nested Dialogs
window.open_dialog(cx, |dialog, _, _| {
dialog
.title("First Dialog")
.child("This is the first dialog")
.footer(|_, _, _, _| {
vec![
Button::new("open-another")
.label("Open Another Dialog")
.on_click(|_, window, cx| {
window.open_dialog(cx, |dialog, _, _| {
dialog
.title("Second Dialog")
.child("This is nested")
});
}),
]
})
})
#Custom Styling
window.open_dialog(cx, |dialog, _, cx| {
dialog
.rounded(cx.theme().radius_lg)
.bg(cx.theme().cyan)
.text_color(cx.theme().info_foreground)
.title("Custom Style")
.child("Styled dialog content")
})
#Custom Padding
window.open_dialog(cx, |dialog, _, _| {
dialog
.p_3() // Custom padding
.title("Custom Padding")
.child("Dialog with custom spacing")
})
#Close Dialog Programmatically
The close_dialog method can be used to close the active dialog from anywhere within the window context.
// Close top level active dialog.
window.close_dialog(cx);
// Close and perform action
Button::new("submit")
.primary()
.label("Submit")
.on_click(|_, window, cx| {
// Do something
window.close_dialog(cx);
})
#Declarative API
The Dialog component now supports a declarative API that provides a more React-like component composition pattern using dedicated header, title, description, and footer components.
#Import
use gpui_kit::component::dialog::{
Dialog, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
};
#Trigger-based Dialog
The trigger-based approach allows you to create a dialog that opens when a trigger element is clicked. The dialog is defined inline with the trigger.
Dialog::new(cx)
.trigger(
Button::new("open-dialog")
.outline()
.label("Open Dialog")
)
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Account Created"))
.child(DialogDescription::new().child(
"Your account has been created successfully!",
))
)
.child(
DialogFooter::new()
.border_t_1()
.border_color(cx.theme().border)
.bg(cx.theme().muted)
.child(
Button::new("cancel")
.outline()
.label("Cancel")
.on_click(|_, window, cx| {
window.close_dialog(cx);
})
)
.child(
Button::new("ok")
.primary()
.label("Save Changes")
)
)
})
#Content Builder Pattern
Use the content builder pattern with window.open_dialog for more control over dialog creation:
window.open_dialog(cx, |dialog, _, _| {
dialog
.w(px(400.))
.content(|content, _, _| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Custom Width"))
.child(DialogDescription::new().child(
"This dialog has a custom width of 400px.",
))
)
.child(div().child(
"Content area with custom width configuration."
))
.child(
DialogFooter::new()
.justify_center()
.child(
Button::new("cancel")
.flex_1()
.outline()
.label("Cancel")
.on_click(|_, window, cx| {
window.close_dialog(cx);
})
)
.child(
Button::new("done")
.flex_1()
.primary()
.label("Done")
.on_click(|_, window, cx| {
window.close_dialog(cx);
})
)
)
})
})
#Declarative Components
#DialogHeader
Container for the dialog’s title and description section.
DialogHeader::new()
.child(DialogTitle::new().child("Title"))
.child(DialogDescription::new().child("Description"))
#DialogTitle
Displays the main title of the dialog with semantic styling.
DialogTitle::new()
.child("Account Settings")
#DialogDescription
Displays descriptive text below the title with muted styling.
DialogDescription::new()
.child("Update your account settings and preferences here.")
#DialogFooter
Container for action buttons and footer content. Automatically applies proper spacing and alignment.
DialogFooter::new()
.bg(cx.theme().muted)
.border_t_1()
.border_color(cx.theme().border)
.child(Button::new("cancel").outline().label("Cancel"))
.child(Button::new("save").primary().label("Save"))
#Form Dialog with Declarative API
let name_input = cx.new(|cx| InputState::new(window, cx));
let email_input = cx.new(|cx| InputState::new(window, cx));
Dialog::new(cx)
.trigger(Button::new("edit-profile").label("Edit Profile"))
.content(|content, _, cx| {
content
.child(
DialogHeader::new()
.child(DialogTitle::new().child("Edit Profile"))
.child(DialogDescription::new().child(
"Make changes to your profile here. Click save when done."
))
)
.child(
v_flex()
.gap_4()
.py_4()
.child(
v_flex()
.gap_2()
.child("Name")
.child(Input::new(&name_input).placeholder("Enter your name"))
)
.child(
v_flex()
.gap_2()
.child("Email")
.child(Input::new(&email_input).placeholder("Enter your email"))
)
)
.child(
DialogFooter::new()
.child(Button::new("cancel").outline().label("Cancel"))
.child(Button::new("save").primary().label("Save Changes"))
)
})
#Styled Footer
Customize the footer appearance with background colors, borders, and alignment:
DialogFooter::new()
.justify_center() // Center align buttons
.bg(cx.theme().muted) // Background color
.border_t_1() // Top border
.border_color(cx.theme().border)
.child(Button::new("btn1").flex_1().label("Cancel"))
.child(Button::new("btn2").flex_1().primary().label("Confirm"))
#DialogContent Container
The DialogContent component provides a flexible container for dialog body content:
use gpui_kit::component::dialog::DialogContent;
window.open_dialog(cx, |dialog, _, _| {
dialog.content(|content, _, cx| {
content
.child(DialogHeader::new()
.child(DialogTitle::new().child("Settings"))
.child(DialogDescription::new().child("Configure your preferences"))
)
.child(
div()
.py_4()
.child("Main content area")
)
.child(DialogFooter::new()
.child(Button::new("close").label("Close"))
)
})
})
#API Reference - Declarative Components
#Dialog
| Method | Description |
|---|---|
new(cx) | Create a new Dialog (no longer requires window param) |
trigger(element) | Set trigger element that opens the dialog |
content(builder) | Set content using a builder function |
w(px) / width(px) | Set dialog width |
max_w(px) | Set maximum width |
margin_top(px) | Set top margin |
entrance(DialogEntrance) | Set entrance animation (default: SlideDown) |
overlay(bool) | Show/hide overlay (default: true) |
overlay_closable(bool) | Allow closing by clicking overlay (default: true) |
keyboard(bool) | Allow closing with ESC key (default: true) |
close_button(bool) | Show/hide close button (default: true) |
#DialogContent
Container for dialog body content. Automatically applies padding and flex layout.
DialogContent::new()
.child(DialogHeader::new()...)
.child(/* your content */)
.child(DialogFooter::new()...)
#DialogHeader
Container for title and description. Automatically applies vertical flex layout with proper gap.
DialogHeader::new()
.child(DialogTitle::new().child("Title"))
.child(DialogDescription::new().child("Description"))
#DialogTitle
Displays the dialog title with semantic styling (font-semibold, proper line-height).
DialogTitle::new()
.child("Dialog Title")
#DialogDescription
Displays descriptive text with muted foreground color and proper text sizing.
DialogDescription::new()
.child("This is a description text that provides more context.")
#DialogFooter
Container for footer buttons with automatic spacing and alignment.
DialogFooter::new()
.justify_end() // Right align (default)
.child(Button::new("btn1").label("Cancel"))
.child(Button::new("btn2").primary().label("OK"))
#Breaking Changes
#Dialog::new() Signature Change
The Dialog::new() constructor no longer requires a window parameter:
// Old API (deprecated)
Dialog::new(window, cx)
// New API
Dialog::new(cx)
#Content Builder Function
The .content() method now accepts a builder function instead of a pre-built DialogContent:
// Old approach (still works)
dialog.child(DialogHeader::new()...)
// New declarative API
dialog.content(|content, window, cx| {
content
.child(DialogHeader::new()...)
.child(DialogFooter::new()...)
})
#Best Practices
- Use Declarative Components: Prefer
DialogHeader,DialogTitle,DialogDescription, andDialogFooterfor consistent styling - Trigger-based for Simple Cases: Use the trigger pattern for straightforward dialogs that open from a button
- Builder Pattern for Complex Dialogs: Use
window.open_dialogwith content builder for dialogs requiring complex logic or state - Semantic Structure: Always include
DialogHeaderwith title and description for accessibility - Consistent Footer: Use
DialogFooterfor all action buttons to maintain visual consistency - Proper Sizing: Explicitly set dialog width when content requires specific dimensions