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

Input

rx.inputrx.input.slot

Official docs

Workshop order

Build a newsletter sign-up field. It takes one line, an email address, and must refuse to submit when the format is wrong.

rx.input is a single-line text field. Use it for short values such as a search term, a name, an email address or a password. When you need several lines, use Text Area.

rx.input(placeholder="Search the docs")

Try the props

Change type and the same field becomes a password field, a date picker or a color picker. The other props control how it looks.

Try the props

placeholder

type

variant

size

color

radius

disabled

read_only

rx.input(placeholder="Search the docs", type="text", variant="surface", size="2", color_scheme="indigo", radius="medium", disabled=False, read_only=False)
variant has three options: classic, surface and soft. read_only only displays the value; disabled dims the field and blocks focus too.
PropValuesDefaultUse
placeholderstringnoneDimmed hint shown while the field is empty
type"text" "password" "email" "number" "date" …"text"Kind of input, same as HTML <input type>
value / default_valuestring, numbernoneControlled value / value set only once at the start
variant"classic" "surface" "soft""surface"Look
size"1" – "3""2"Height and font size
color_scheme, radiusRadix color name / "none" – "full"theme valueAccent color and corners
name, requiredstring / TruenoneName used on form submit; block submit while empty
max_length, min_length, patternnumber / regexnoneLength and format checked by the browser
min, max, stepnumbernoneRange and step for type="number", "date" and so on
disabled, read_only, auto_focusTrue / FalseFalseDisable, read-only, focus as soon as the page opens
auto_completeTrue / Falsebrowser valueBrowser autocomplete. The official docs also list strings like "off", but 0.10 only accepts a bool
on_changeeventWhen the value changes. Passes the new value (a string)
on_blur, on_focuseventWhen focus is lost / gained. on_blur passes the value too
on_key_down, on_key_upeventWhen a key is pressed / released. Passes the key name

on_blur: once, when you leave

on_blur fires once when focus leaves the field. Click outside or Tab away and the current value goes to the State. It fits when you don't need to call the server on every keystroke.

Hello World!

class InputBlurState(rx.State):
    text: str = "Hello World!"

    @rx.event
    def set_text(self, value: str):
        self.text = value


rx.vstack(
    rx.heading(InputBlurState.text, size="5"),
    rx.input(placeholder="Type, then click outside", on_blur=InputBlurState.set_text),
)

Controlled input: value + on_change

Give value a State value and update it with on_change, and the field always matches the State. This is called a controlled input. When the server changes the value, the field follows.

When rx.input gets both value and on_change, it wraps itself in a debounced input (DebounceInput). While you type, the text shows up right away, and it is sent to the server once you pause for a moment (300 ms by default). That is why typing never stutters even though the State holds the value.

Hello World!

class InputControlledState(rx.State):
    text: str = "Hello World!"

    @rx.event
    def set_text(self, value: str):
        self.text = value


rx.vstack(
    rx.heading(InputControlledState.text, size="5"),
    rx.input(
        placeholder="Type here",
        value=InputControlledState.text,
        on_change=InputControlledState.set_text,
    ),
)

Using only on_change or on_blur, without value, gives an uncontrolled input. Set the initial value with default_value; after that the browser holds the value.

Slots: adding icons and text

Put rx.input.slot in as a child and an icon or a short piece of text appears inside the field. With side="right" it sits on the right. A slot also takes its own color_scheme.

@example.com

rx.vstack(
    rx.input(
        rx.input.slot(rx.icon("search", size=16)),
        placeholder="Search the docs",
    ),
    rx.input(
        rx.input.slot(rx.icon("at-sign", size=16)),
        rx.input.slot(rx.text("@example.com", size="1"), side="right"),
        placeholder="username",
    ),
)

Input types (type)

type takes the same values as HTML <input type>: "text" (the default), "password", "email", "number", "date", "time", "url", "color" and more, and the browser shows a matching input widget. The full list is in MDN's input documentation.

rx.vstack(
    rx.input(placeholder="Username", type="text"),
    rx.input(placeholder="Password", type="password"),
    rx.input(placeholder="Email", type="email"),
    rx.input(type="number", min=0, max=10, step=1, default_value="3"),
    rx.input(type="date"),
    rx.input(type="color", default_value="#3e63dd", width="80px"),
)

Submitting with a form

An input inside a form needs a name to end up in the submitted data as name: value. With required=True the browser blocks submitting while the field is empty. The browser also checks pattern and max_length before submitting. The second field below only accepts exactly three digits.

Results:

{}
class InputFormState(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.hstack(
                rx.input(name="username", placeholder="Username", required=True),
                rx.input(name="code", placeholder="3 digits", pattern="[0-9]{3}", max_length=3),
                rx.button("Submit", type="submit"),
            ),
            on_submit=InputFormState.handle_submit,
            reset_on_submit=True,
        ),
        rx.divider(),
        rx.hstack(rx.text("Results:", weight="bold"), rx.badge(InputFormState.form_data.to_string())),
    ),
)

Forms themselves are covered in detail on the Form page.

Changing the value without State: rx.set_value

rx.set_value("id", value) sets the value of the input with that id directly. Use it to clear or fill a field without creating a State variable.

rx.hstack(
    rx.input(id="input_erase_demo", default_value="Erase me"),
    rx.button("Erase", on_click=rx.set_value("input_erase_demo", "")),
    rx.button("Fill", on_click=rx.set_value("input_erase_demo", "Hello"), variant="soft"),
)

Keyboard events

on_key_down passes the key name ("Enter", "a" …) every time a key is pressed. If you want Enter to trigger something, it is usually simpler to put the input in a form.

Last key: -

class InputKeyState(rx.State):
    last_key: str = "-"

    @rx.event
    def on_key(self, key: str):
        self.last_key = key


rx.vstack(
    rx.input(placeholder="Press any key", on_key_down=InputKeyState.on_key),
    rx.text("Last key: ", rx.code(InputKeyState.last_key)),
)

Filling the order

type="email" and required=True let the browser check the format first, and the form handles both Enter and the button. Only the three most recent addresses are shown.

class InputNewsletterState(rx.State):
    emails: list[str] = []

    @rx.event
    def subscribe(self, form_data: dict):
        email = form_data.get("email", "").strip()
        if email and email not in self.emails:
            self.emails = (self.emails + [email])[-3:]


rx.vstack(
    rx.form(
        rx.hstack(
            rx.input(
                rx.input.slot(rx.icon("mail", size=16)),
                name="email",
                type="email",
                placeholder="you@example.com",
                required=True,
                flex="1",
            ),
            rx.button("Subscribe", type="submit"),
            width="100%",
        ),
        on_submit=InputNewsletterState.subscribe,
        reset_on_submit=True,
        width="100%",
    ),
    rx.foreach(InputNewsletterState.emails, lambda e: rx.badge(e, variant="soft")),
    width="100%",
    max_width="420px",
)

Workshop notes

  • Create it with rx.input(placeholder=...) and pick the kind of input with type.

  • on_blur fires once when you leave; value + on_change makes a controlled input (debounced automatically).

  • Icons go in rx.input.slot; side="right" puts them on the right.

  • In a form, name becomes the key, and the browser checks required, pattern and max_length.

  • To change the value without State, use rx.set_value("id", value).

← Previous

Forms · Button

Next →

Forms · Text Area

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