R

ReflexKR

Reflex Docs

The Component Workshop: Reflex's 100 Library Components

·

Course overview

Layout

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

Typography

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

Forms

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

Data display

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

Disclosure

·

Tabs

·

Accordion

·

Segmented Control

Overlay

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

Dynamic rendering

·

Cond

·

Match

·

Foreach

·

Auto Scroll

Media

·

Image

·

Audio

·

Video

Tables and data grids

·

Table

·

Data Table

·

Data Editor

Charts

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

Chart parts

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

Other chart libraries

·

Plotly

·

Pyplot

HTML elements

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

Other tools

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

The Component Workshop: Reflex's 100 Library Components
Overlay

Alert Dialog

rx.alert_dialog.rootrx.alert_dialog.triggerrx.alert_dialog.contentrx.alert_dialog.titlerx.alert_dialog.descriptionrx.alert_dialog.actionrx.alert_dialog.cancel

Official docs

Workshop order

When someone clicks the trash button in the project list, don't delete right away. Open a window that asks once more whether they really want to delete it.

rx.alert_dialog is a confirmation window that interrupts the user and waits for an answer. Unlike a regular Dialog, it does not close when you click outside it, so the user has to choose either 'Cancel' or the action (Esc counts as cancel). Put it in front of things that are hard to undo, such as deleting something or revoking access.

  • alert_dialog.root: wraps all the parts.
  • alert_dialog.trigger: wraps the element that opens the window.
  • alert_dialog.content: the window's content.
  • alert_dialog.title: the window's title. Screen readers announce it when the window opens.
  • alert_dialog.description: an optional extra explanation, read out along with the title.
  • alert_dialog.action: wraps the action button. Clicking it closes the window.
  • alert_dialog.cancel: wraps the cancel button. Clicking it closes the window. Make it visually distinct from the action button.
rx.alert_dialog.root(
    rx.alert_dialog.trigger(
        rx.button("Revoke access"),
    ),
    rx.alert_dialog.content(
        rx.alert_dialog.title("Revoke access"),
        rx.alert_dialog.description(
            "Are you sure? This application will no longer be accessible and any existing sessions will be expired.",
        ),
        rx.flex(
            rx.alert_dialog.cancel(
                rx.button("Cancel"),
            ),
            rx.alert_dialog.action(
                rx.button("Revoke access"),
            ),
            spacing="3",
        ),
    ),
)

Try the props

The window's padding comes from size on content, and the button layout from justify on the surrounding rx.flex. Give the action button a color that matches how serious the action is.

Try the props

title

action

color

size

justify

rx.alert_dialog.root(
    rx.alert_dialog.trigger(rx.button("Delete", color_scheme="red")),
    rx.alert_dialog.content(
        rx.alert_dialog.title("Delete this file?"),
        rx.alert_dialog.description("This action cannot be undone.", size="2"),
        rx.flex(
            rx.alert_dialog.cancel(rx.button("Cancel", variant="soft", color_scheme="gray")),
            rx.alert_dialog.action(rx.button("Delete", color_scheme="red")),
            spacing="3",
            margin_top="16px",
            justify="end",
        ),
        size="3",
        max_width="450px",
    ),
)
For a dangerous action, make the action button red and the cancel button gray soft. Buttons usually sit at the right end (justify="end").
PartPropValuesUse
rootopenTrue / FalseDrive the open state from State (controlled)
rootdefault_openTrue / FalseOpen on first render (uncontrolled)
rooton_open_changeeventFires on open or close; passes the new open value (bool)
contentsize"1" ~ "4"Padding and corner size
contentforce_mountTrue / FalseKeep it in the DOM while closed (for animations)
contenton_open_auto_focus · on_close_auto_focuseventFocus moves into the window on open, back to the trigger on close
contenton_escape_key_downeventEsc is pressed

trigger, title, description, action and cancel only have the common events. The 'click outside' events from Dialog's content are missing here, because clicking outside does not close the window.

Color and right alignment

Here the action button and the trigger are red, the cancel button is toned down in gray soft, and both buttons are gathered on the right with justify="end". The window is narrowed with style={"max_width": 450}.

rx.alert_dialog.root(
    rx.alert_dialog.trigger(
        rx.button("Revoke access", color_scheme="red"),
    ),
    rx.alert_dialog.content(
        rx.alert_dialog.title("Revoke access"),
        rx.alert_dialog.description(
            "Are you sure? This application will no longer be accessible and any existing sessions will be expired.",
            size="2",
        ),
        rx.flex(
            rx.alert_dialog.cancel(
                rx.button("Cancel", variant="soft", color_scheme="gray"),
            ),
            rx.alert_dialog.action(
                rx.button("Revoke access", color_scheme="red", variant="solid"),
            ),
            spacing="3",
            margin_top="16px",
            justify="end",
        ),
        style={"max_width": 450},
    ),
)

Run a table out to the window's left and right edges with rx.inset to show at a glance what is about to be deleted.

rx.alert_dialog.root(
    rx.alert_dialog.trigger(
        rx.button("Delete Users", color_scheme="red"),
    ),
    rx.alert_dialog.content(
        rx.alert_dialog.title("Delete Users"),
        rx.alert_dialog.description(
            "Are you sure you want to delete these users? This action is permanent and cannot be undone.",
            size="2",
        ),
        rx.inset(
            rx.table.root(
                rx.table.header(
                    rx.table.row(
                        rx.table.column_header_cell("Full Name"),
                        rx.table.column_header_cell("Email"),
                        rx.table.column_header_cell("Group"),
                    ),
                ),
                rx.table.body(
                    rx.table.row(
                        rx.table.row_header_cell("Danilo Rosa"),
                        rx.table.cell("danilo@example.com"),
                        rx.table.cell("Developer"),
                    ),
                    rx.table.row(
                        rx.table.row_header_cell("Zahra Ambessa"),
                        rx.table.cell("zahra@example.com"),
                        rx.table.cell("Admin"),
                    ),
                ),
            ),
            side="x",
            margin_top="24px",
            margin_bottom="24px",
        ),
        rx.flex(
            rx.alert_dialog.cancel(
                rx.button("Cancel", variant="soft", color_scheme="gray"),
            ),
            rx.alert_dialog.action(
                rx.button("Delete users", color_scheme="red"),
            ),
            spacing="3",
            justify="end",
        ),
        style={"max_width": 500},
    ),
)

Reacting to open and close: on_open_change

The on_open_change of alert_dialog.root passes True when it opens and False when it closes.

Opened or closed: 0 times

Alert dialog open: no

class AlertDialogEventState(rx.State):
    num_opens: int = 0
    opened: bool = False

    @rx.event
    def count_opens(self, value: bool):
        self.opened = value
        self.num_opens += 1


rx.flex(
    rx.text("Opened or closed: ", AlertDialogEventState.num_opens, " times"),
    rx.text("Alert dialog open: ", rx.cond(AlertDialogEventState.opened, "yes", "no")),
    rx.alert_dialog.root(
        rx.alert_dialog.trigger(
            rx.button("Revoke access", color_scheme="red"),
        ),
        rx.alert_dialog.content(
            rx.alert_dialog.title("Revoke access"),
            rx.alert_dialog.description(
                "Are you sure? This application will no longer be accessible and any existing sessions will be expired.",
                size="2",
            ),
            rx.flex(
                rx.alert_dialog.cancel(
                    rx.button("Cancel", variant="soft", color_scheme="gray"),
                ),
                rx.alert_dialog.action(
                    rx.button("Revoke access", color_scheme="red", variant="solid"),
                ),
                spacing="3",
                margin_top="16px",
                justify="end",
            ),
            style={"max_width": 450},
        ),
        on_open_change=AlertDialogEventState.count_opens,
    ),
    direction="column",
    spacing="3",
    align="start",
)

Opening and closing from State

Bind open to a State value and a button outside the window can open it, with no alert_dialog.trigger. The Cancel and action buttons inside flip the value with the same handler to close it. Wire up on_open_change too, so State keeps up when the window is closed with Esc.

The official docs example flips the value with self.opened = ~self.opened, but in Python ~ turns a bool into an integer (-1, -2). This page uses not self.opened instead.

class AlertDialogControlState(rx.State):
    opened: bool = False

    @rx.event
    def dialog_open(self):
        self.opened = not self.opened

    @rx.event
    def set_opened(self, value: bool):
        self.opened = value


rx.box(
    rx.alert_dialog.root(
        rx.alert_dialog.content(
            rx.alert_dialog.title("Revoke access"),
            rx.alert_dialog.description(
                "Are you sure? This application will no longer be accessible and any existing sessions will be expired.",
            ),
            rx.flex(
                rx.alert_dialog.cancel(
                    rx.button("Cancel", on_click=AlertDialogControlState.dialog_open),
                ),
                rx.alert_dialog.action(
                    rx.button("Revoke access", on_click=AlertDialogControlState.dialog_open),
                ),
                spacing="3",
            ),
        ),
        open=AlertDialogControlState.opened,
        on_open_change=AlertDialogControlState.set_opened,
    ),
    rx.button("Button to Open the Dialog", on_click=AlertDialogControlState.dialog_open),
)

Submitting a form

You can add an rx.form the same way as in Dialog. Wrap the submit button in alert_dialog.action and the window closes as the form is submitted. Saving to a database works just like the code on the Dialog page.

class AlertDialogFormState(rx.State):
    current_user: dict[str, str] = {}

    @rx.event
    def add_user(self, form_data: dict):
        self.current_user = form_data
        # Here you would save the user to a database (see the Database course).
        return rx.toast.info(
            f"User {form_data.get('name', '')} has been added.", position="bottom-right"
        )


rx.alert_dialog.root(
    rx.alert_dialog.trigger(
        rx.button(rx.icon("plus", size=26), rx.text("Add User", size="4")),
    ),
    rx.alert_dialog.content(
        rx.alert_dialog.title("Add New User"),
        rx.alert_dialog.description("Fill the form with the user's info"),
        rx.form(
            rx.flex(
                rx.input(placeholder="User Name", name="name"),
                rx.input(placeholder="user@reflex.dev", name="email"),
                rx.flex(
                    rx.alert_dialog.cancel(
                        rx.button("Cancel", variant="soft", color_scheme="gray"),
                    ),
                    rx.alert_dialog.action(
                        rx.button("Submit", type="submit"),
                    ),
                    spacing="3",
                    justify="end",
                ),
                direction="column",
                spacing="4",
            ),
            on_submit=AlertDialogFormState.add_user,
            reset_on_submit=False,
        ),
        max_width="450px",
    ),
)

Filling the order

Each item in the list gets a trash button and a confirmation window. The action button's on_click passes the name to delete, and the handler removes that item from the list and shows a notification. Clicking Cancel or pressing Esc does nothing.

Website

Mobile app

Newsletter

class AlertDialogProjectState(rx.State):
    projects: list[str] = ["Website", "Mobile app", "Newsletter"]

    @rx.event
    def delete_project(self, name: str):
        self.projects = [p for p in self.projects if p != name]
        return rx.toast.success(f"Deleted {name}")

    @rx.event
    def restore(self):
        self.projects = ["Website", "Mobile app", "Newsletter"]


rx.vstack(
    rx.foreach(
        AlertDialogProjectState.projects,
        lambda name: rx.flex(
            rx.text(name),
            rx.spacer(),
            rx.alert_dialog.root(
                rx.alert_dialog.trigger(
                    rx.icon_button(rx.icon("trash-2"), color_scheme="red", variant="ghost", aria_label="Delete"),
                ),
                rx.alert_dialog.content(
                    rx.alert_dialog.title("Delete project?"),
                    rx.alert_dialog.description(
                        "The project ", rx.text.strong(name), " and all its files will be deleted.",
                        size="2",
                    ),
                    rx.flex(
                        rx.alert_dialog.cancel(rx.button("Cancel", variant="soft", color_scheme="gray")),
                        rx.alert_dialog.action(
                            rx.button("Delete", color_scheme="red", on_click=AlertDialogProjectState.delete_project(name)),
                        ),
                        spacing="3",
                        margin_top="16px",
                        justify="end",
                    ),
                    max_width="420px",
                ),
            ),
            width="100%",
            align="center",
            padding="6px 10px",
            border=f"1px solid {rx.color('gray', 5)}",
            border_radius="6px",
        ),
    ),
    rx.button("Restore all", size="1", variant="soft", on_click=AlertDialogProjectState.restore),
    width="100%",
    max_width="360px",
)

Workshop notes

  • root · trigger · content · title · description · action · cancel. Clicking outside does not close it.

  • action and cancel both close the window. Wire the real work to on_click of the button inside action.

  • Give the action button a color that matches how serious it is (red for delete) and set cancel apart in gray soft.

  • When you control it with open, wire up on_open_change too, so closing with Esc gets back to State.

  • If the action can be undone, consider doing it right away and showing an 'Undo' notification (Toast) instead of a confirmation window.

← Previous

Overlay · Dialog

Next →

Overlay · Drawer

Get news about new lessons

We'll email you when this course gets new lessons or a follow-up course opens.

We collect: your email address · Purpose: news about new lessons or the launch of this course · Kept until: you ask us to remove it. We never use it for anything else or share it. Reply to any notice email and we'll delete it right away.

Questions & answers

Ask anything about this lesson. Questions from every language version of this page appear together.

Shown as plain text, without links or formatting.

R

ReflexKR

Learn Python and Reflex by doing. Every example was run and verified on Reflex 0.10.0.

ReflexKR is free, with no banner ads. If it helped you, you can support it with a coffee. ☕ Buy us a coffee

Affiliate disclosure
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex