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
rx.alert_dialog.rootrx.alert_dialog.triggerrx.alert_dialog.contentrx.alert_dialog.titlerx.alert_dialog.descriptionrx.alert_dialog.actionrx.alert_dialog.cancelOfficial 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",
),
),
)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",
),
)| Part | Prop | Values | Use |
|---|---|---|---|
root | open | True / False | Drive the open state from State (controlled) |
root | default_open | True / False | Open on first render (uncontrolled) |
root | on_open_change | event | Fires on open or close; passes the new open value (bool) |
content | size | "1" ~ "4" | Padding and corner size |
content | force_mount | True / False | Keep it in the DOM while closed (for animations) |
content | on_open_auto_focus · on_close_auto_focus | event | Focus moves into the window on open, back to the trigger on close |
content | on_escape_key_down | event | Esc 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.
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},
),
)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",
)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),
)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",
),
)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.
Get news about new lessons
We'll email you when this course gets new lessons or a follow-up course opens.
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© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Questions & answers
Ask anything about this lesson. Questions from every language version of this page appear together.