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
Forms

Radio Group

rx.radio_grouprx.radio_group.rootrx.radio_group.item

Official docs

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

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)
direction is the direction the options are laid out in, spacing the gap between them. Both exist only on the high-level rx.radio_group.
PropValuesDefaultUse
first argument (items)list of strings / VarrequiredOptions. Value and label are the same
valuestring / VarnoneControlled selection (with on_change)
default_valuestring""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_contrastRadix color name / Truetheme valueColor and contrast
disabled, required, nameDisable, must pick to submit, form field name
on_changeeventWhen the selected option changes. Passes its value (a string)

Getting the selection with on_change

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.

No Selection
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"),
)

Controlled radio group: value + on_change

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",
    ),
)

Subcomponents: root and item

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",
)

Submitting with a form

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",
    ),
)

Filling the order

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.

← Previous

Forms · Switch

Next →

Forms · Select

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