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

Text Area

rx.text_area

Official docs

Workshop order

Add a review box to the order details screen. Up to 200 characters, with the remaining count visible, and Enter should post it right away.

rx.text_area is a multi-line text field. Use it for text that may contain line breaks: comments, support requests, notes. It works almost exactly like rx.input, plus a line count (rows), resizing (resize) and automatic height (auto_height).

rx.text_area(placeholder="Type here...")

Try the props

Turn on auto_height and type a few lines: the field grows with the text. resize sets the directions in which the user can resize it with the handle in the bottom-right corner.

Try the props

placeholder

rows

resize

variant

size

color

radius

auto_height

disabled

rx.text_area(placeholder="Write a comment...", rows="3", resize="vertical", variant="surface", size="2", color_scheme="indigo", radius="medium", auto_height=False, disabled=False)
rows is the number of lines shown at first; resize is the direction the user can stretch it. variant, size and radius work as in Input.
PropValuesDefaultUse
placeholderstringnoneHint shown while the field is empty
value / default_valuestringnoneControlled value / value set only once at the start
rows, colsnumber as stringbrowser valueLines shown at first, width in characters
resize"none" "vertical" "horizontal" "both"theme valueDirections in which the user can resize
auto_heightTrue / FalseFalseGrows with the text (together with min_height)
enter_key_submitTrue / False / VarFalseEnter submits the form, Shift+Enter adds a line break
variant, size, color_scheme, radiussame as InputLook
name, required, max_length, min_lengthnoneForm field name and browser checks
disabled, read_only, auto_focusTrue / FalseFalseDisable, read-only, autofocus
on_change, on_blureventWhen the value changes / focus is lost. Passes the value
on_key_down, on_key_upeventWhen a key is pressed / released

Basics: getting the value with on_blur

As with Input, on_blur passes the text written so far once, when you leave the field. Give it value and on_change together and it becomes a controlled input, again with debouncing added automatically.

Hello World!

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

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


rx.vstack(
    rx.heading(TextAreaBlurState.text, size="4"),
    rx.text_area(placeholder="Type, then click outside", on_blur=TextAreaBlurState.set_text),
    width="100%",
)

Inside a form

In form data, name becomes the key. Below is the feedback form from the official docs. The State holds the field through value + on_change, and after sending, rx.cond swaps it for a thank-you card. Because of required=True, an empty message can't be sent.

Are you enjoying Reflex?

class TextAreaFeedbackState(rx.State):
    feedback: str = ""
    submitted: bool = False

    @rx.event
    def set_feedback(self, value: str):
        self.feedback = value

    @rx.event
    def submit_feedback(self, form_data: dict):
        self.submitted = True

    @rx.event
    def reset_form(self):
        self.feedback = ""
        self.submitted = False


rx.cond(
    TextAreaFeedbackState.submitted,
    rx.card(
        rx.vstack(
            rx.text("Thank you! You wrote:"),
            rx.blockquote(TextAreaFeedbackState.feedback),
            rx.button("Submit another response", on_click=TextAreaFeedbackState.reset_form),
        ),
    ),
    rx.card(
        rx.form(
            rx.flex(
                rx.text("Are you enjoying Reflex?"),
                rx.text_area(
                    name="feedback",
                    placeholder="Write your feedback...",
                    value=TextAreaFeedbackState.feedback,
                    on_change=TextAreaFeedbackState.set_feedback,
                    resize="vertical",
                    required=True,
                ),
                rx.button("Send", type="submit"),
                direction="column",
                spacing="3",
            ),
            on_submit=TextAreaFeedbackState.submit_feedback,
        ),
    ),
)

Submitting with Enter

In a multi-line field, Enter normally inserts a line break. With enter_key_submit=True, Enter submits the surrounding form and Shift+Enter becomes the line break. The prop also accepts a Var, so you can toggle it with a switch as below. To turn it off while something is processing, write enter_key_submit=~State.is_loading.

enter_key_submit uses its own key handler, so it can't be combined with on_key_down (doing so raises a ValueError). A Python on_key_down handler only receives the key name, so it can't prevent the line break either. If you need both, you need browser-side handling such as rx.call_script.

Enter submits

Nothing submitted yet.

class TextAreaEnterState(rx.State):
    submitted_text: str = ""
    enter_submits: bool = True

    @rx.event
    def handle_submit(self, form_data: dict):
        self.submitted_text = form_data.get("message", "")

    @rx.event
    def set_enter_submits(self, value: bool):
        self.enter_submits = value


rx.vstack(
    rx.hstack(
        rx.switch(checked=TextAreaEnterState.enter_submits, on_change=TextAreaEnterState.set_enter_submits),
        rx.text("Enter submits", size="2"),
        align="center",
    ),
    rx.form(
        rx.text_area(
            name="message",
            placeholder="Type and press Enter (Shift+Enter for a new line)",
            enter_key_submit=TextAreaEnterState.enter_submits,
        ),
        on_submit=TextAreaEnterState.handle_submit,
        reset_on_submit=True,
        width="100%",
    ),
    rx.cond(
        TextAreaEnterState.submitted_text != "",
        rx.text("Submitted: ", rx.code(TextAreaEnterState.submitted_text)),
        rx.text("Nothing submitted yet.", color_scheme="gray"),
    ),
    width="100%",
)

Automatic height

With auto_height=True, the field grows with the text instead of scrolling inside. Add rows="1" and min_height="0" and it starts at one line and grows from there. To stop it growing too much, give it a max_height. The one below stops at 160px.

rx.text_area(
    placeholder="Type a few lines - the box grows to fit",
    auto_height=True,
    rows="1",
    min_height="0",
    max_height="160px",
    width="100%",
)

Filling the order

The State holds the text through value + on_change, and the computed var left shows how many characters remain. The browser limits the length with max_length, and the server limits it again with [:200]. On save, it uses the value from form_data, so no characters still held back by the debounce are lost. auto_height lets it grow, and enter_key_submit posts it when you press Enter.

200 characters left

class TextAreaReviewState(rx.State):
    review: str = ""
    saved: str = ""

    @rx.event
    def set_review(self, value: str):
        self.review = value[:200]

    @rx.event
    def save(self, form_data: dict):
        self.saved = form_data.get("review", "")[:200]
        self.review = ""

    @rx.var
    def left(self) -> int:
        return 200 - len(self.review)


rx.form(
    rx.vstack(
        rx.text_area(
            name="review",
            placeholder="How was your order?",
            value=TextAreaReviewState.review,
            on_change=TextAreaReviewState.set_review,
            max_length=200,
            auto_height=True,
            rows="2",
            min_height="0",
            enter_key_submit=True,
            width="100%",
        ),
        rx.hstack(
            rx.text(TextAreaReviewState.left, " characters left", size="1", color_scheme="gray"),
            rx.spacer(),
            rx.button("Post review", type="submit", disabled=TextAreaReviewState.review == ""),
            width="100%",
            align="center",
        ),
        rx.cond(TextAreaReviewState.saved != "", rx.callout(TextAreaReviewState.saved, icon="message-square")),
        width="100%",
    ),
    on_submit=TextAreaReviewState.save,
    width="100%",
    max_width="460px",
)

Workshop notes

  • rx.text_area is multi-line input. It works like rx.input (on_blur, value + on_change).

  • rows sets the initial line count; resize sets the directions the user can stretch it.

  • auto_height=True + rows="1" + min_height="0" makes it grow with the text. Cap it with max_height.

  • enter_key_submit=True makes Enter submit the form and Shift+Enter add a line break. It can't be combined with on_key_down.

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

← Previous

Forms · Input

Next →

Forms · Checkbox

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