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
Workshop order
Add a field to the checkout screen for picking one of three shipping methods, and show the shipping cost of the chosen method right away.
rx.radio_group is a set of options where you can pick exactly one. Use it when there are about five options or fewer and all of them should be visible at a glance. When there are many options and you need to save space, use Select. Pass a list and each option gets its own circle and label.
rx.radio_group(["1", "2", "3"], default_value="1")Try the props
direction
spacing
size
variant
color
high_contrast
disabled
rx.radio_group(["Small", "Medium", "Large"], default_value="Medium", direction="row", spacing="2", size="2", variant="classic", color_scheme="indigo", high_contrast=False, disabled=False)| Prop | Values | Default | Use |
|---|---|---|---|
first argument (items) | list of strings / Var | required | Options. Value and label are the same |
value | string / Var | none | Controlled selection (with on_change) |
default_value | string | "" | Selection set only once at the start |
direction | "row" "column" "row-reverse" "column-reverse" | "row" | Layout direction (high-level only) |
spacing | "0" – "9" | "2" | Gap between options (high-level only) |
size | "1" – "3" | "2" | Size |
variant | "classic" "surface" "soft" | "classic" | Look |
color_scheme, high_contrast | Radix color name / True | theme value | Color and contrast |
disabled, required, name | Disable, must pick to submit, form field name | ||
on_change | event | When the selected option changes. Passes its value (a string) |
Every time you pick an option, on_change passes that option's value. rx.radio is another name for rx.radio_group, so writing rx.radio([...]) as the official docs do works the same.
class RadioGroupBasicState(rx.State):
item: str = "No Selection"
@rx.event
def set_item(self, item: str):
self.item = item
rx.vstack(
rx.badge(RadioGroupBasicState.item, color_scheme="green"),
rx.radio(["1", "2", "3"], on_change=RadioGroupBasicState.set_item, direction="row"),
)Give value a State value and the selection always follows it. When a button changes the State, the selection moves too. With only default_value and no value, it is uncontrolled: you set the initial selection, and after that the browser holds it.
Selected: M
class RadioGroupControlState(rx.State):
size: str = "M"
@rx.event
def set_size(self, value: str):
self.size = value
rx.vstack(
rx.radio_group(
["S", "M", "L", "XL"],
value=RadioGroupControlState.size,
on_change=RadioGroupControlState.set_size,
),
rx.hstack(
rx.button("Pick XL", on_click=RadioGroupControlState.set_size("XL"), size="1", variant="soft"),
rx.text("Selected: ", rx.code(RadioGroupControlState.size), size="2"),
align="center",
),
)When the label should differ from the value, when you want to disable just one option, or when you want to arrange things yourself, use the low-level API. rx.radio_group.root handles the group and the selection (value, default_value, on_change, name), and rx.radio_group.item is a single circle. An item only takes value, disabled and required. Add labels by wrapping with rx.text(..., as_="label").
rx.radio_group.root(
rx.flex(
rx.text(rx.flex(rx.radio_group.item(value="card"), "Credit card", spacing="2"), as_="label", size="2"),
rx.text(rx.flex(rx.radio_group.item(value="bank"), "Bank transfer", spacing="2"), as_="label", size="2"),
rx.text(
rx.flex(rx.radio_group.item(value="cash", disabled=True), "Cash (unavailable)", spacing="2"),
as_="label",
size="2",
color_scheme="gray",
),
direction="column",
spacing="2",
),
default_value="card",
color_scheme="teal",
)name is the group's name and goes into the form data as name: selected value. If nothing is selected, the key is left out. With required=True, an option must be picked before the form submits.
Results:
{}class RadioGroupFormState(rx.State):
form_data: dict = {}
@rx.event
def handle_submit(self, form_data: dict):
self.form_data = form_data
rx.card(
rx.vstack(
rx.form.root(
rx.vstack(
rx.radio_group(
["Option 1", "Option 2", "Option 3"],
name="radio_choice",
required=True,
direction="row",
),
rx.button("Submit", type="submit"),
spacing="4",
),
on_submit=RadioGroupFormState.handle_submit,
reset_on_submit=True,
),
rx.divider(),
rx.hstack(rx.text("Results:", weight="bold"), rx.badge(RadioGroupFormState.form_data.to_string())),
spacing="4",
),
)The shipping cost table is a constant dict, and the computed var fee looks up the cost of the chosen method. The options are built from the same dict's keys, so adding a row to the table adds an option as well.
Shipping
Fee: 0 KRW
RADIO_SHIPPING_FEES = {"Standard": 0, "Express": 3000, "Same day": 7000}
class RadioGroupShippingState(rx.State):
speed: str = "Standard"
@rx.event
def set_speed(self, value: str):
self.speed = value
@rx.var
def fee(self) -> int:
return RADIO_SHIPPING_FEES.get(self.speed, 0)
rx.card(
rx.vstack(
rx.text("Shipping", weight="bold"),
rx.radio_group(
list(RADIO_SHIPPING_FEES),
value=RadioGroupShippingState.speed,
on_change=RadioGroupShippingState.set_speed,
direction="column",
),
rx.divider(),
rx.text("Fee: ", RadioGroupShippingState.fee, " KRW", size="2"),
),
width="240px",
)Workshop notes
rx.radio_group([options...]) picks exactly one. Use it instead of Select when there are few options.
on_change passes the chosen value. Control it with value + on_change; set only the initial value with default_value.
direction and spacing exist only on the high-level component. For fine-grained layout and labels, use root + item.
In a form, it is sent as name: chosen value. With required, something must be picked to submit.
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.