R

ReflexKR

Reflex 官方文档

组件工坊:Reflex 组件库 100 例

·

课程概览

布局

·

Box

·

Flex

·

Stack

·

Grid

·

Container

·

Section

·

Center

·

Spacer

·

Separator

·

Card

·

Inset

·

Aspect Ratio

·

Fragment

排版

·

Text

·

Heading

·

Link

·

Code

·

Markdown

·

Blockquote

·

Quote

·

Strong

·

Em

·

Kbd

表单

·

Button

·

Input

·

Text Area

·

Checkbox

·

Switch

·

Radio Group

·

Select

·

Slider

·

Form

·

Upload

数据展示

·

Badge

·

Avatar

·

Icon

·

Callout

·

List

·

Data List

·

Code Block

·

Progress

·

Spinner

·

Scroll Area

·

Moment

展开与切换

·

Tabs

·

Accordion

·

Segmented Control

浮层

·

Tooltip

·

Popover

·

Hover Card

·

Dialog

·

Alert Dialog

·

Drawer

·

Dropdown Menu

·

Context Menu

·

Toast

动态渲染

·

Cond

·

Match

·

Foreach

·

Auto Scroll

媒体

·

Image

·

Audio

·

Video

表格与数据网格

·

Table

·

Data Table

·

Data Editor

图表

·

Area Chart

·

Bar Chart

·

Line Chart

·

Composed Chart

·

Pie Chart

·

Radar Chart

·

Radial Bar Chart

·

Scatter Chart

·

Funnel Chart

·

Treemap

·

Sankey Chart

·

Error Bar

图表部件

·

Axis

·

Cartesian Grid

·

Chart Tooltip

·

Legend

·

Label

·

Reference

·

Brush

其他图表库

·

Plotly

·

Pyplot

HTML 元素

·

HTML

·

HTML Text

·

HTML Layout

·

HTML Forms

·

HTML Media

·

HTML Tables

·

SVG

其他工具

·

Theme

·

Skeleton

·

Clipboard

·

Html Embed

·

Script

·

Memo

组件工坊:Reflex 组件库 100 例
HTML 元素

HTML Forms

rx.el.fieldsetrx.el.formrx.el.labelrx.el.legendrx.el.meterrx.el.optgrouprx.el.optionrx.el.outputrx.el.progressrx.el.selectrx.el.textarea

官方文档

工坊订单

咖啡店点单表单:饮品从按类别分组的列表中选,取餐方式用单选,备注用多行输入,下单后显示订单摘要。

这些是与表单相关的 HTML 元素:收集输入的 form,分组框 fieldset 及其标题 legend,选择用的 select·optgroup·option,多行输入 textarea,以及显示数值的 meter·progress·output。 它们比 Radix 表单组件(rx.select、rx.text_area)更轻量,直接使用浏览器自带的行为。

rx.el.label(
    "Size ",
    rx.el.select(
        rx.el.option("Small", value="s"),
        rx.el.option("Medium", value="m"),
        rx.el.option("Large", value="l"),
        default_value="m",
        border="1px solid gray",
        padding="2px 6px",
    ),
)

试着改属性

meter 会根据值处于好区间还是坏区间改变颜色。low·high 是区间的边界,optimum 决定哪一边是好的。

试着改属性

value

65

low

30

high

80

optimum

90

rx.el.div(
    rx.el.meter(value=65, min=0, max=100, low=30, high=80, optimum=90, width="260px", height="20px"),
    rx.el.progress(value=65, max=100, width="260px"),
    display="flex",
    flex_direction="column",
    gap="10px",
)
optimum 小于 low 时视为值越低越好,大于 high 时视为值越高越好。progress 只是进度条,颜色不会变。

试着改属性

placeholder

rows

resize

disabled

read_only

rx.el.textarea(
    placeholder="Write a note",
    rows="4",
    resize="vertical",
    disabled=False,
    read_only=False,
    border="1px solid gray",
    border_radius="6px",
    padding="6px 8px",
    width="280px",
)
rows 是显示的行数。resize 是 CSS 属性,决定用户能否拖动角落调整大小。
属性取值默认值用途
on_submit (form)事件处理器无(只阻止提交)接收按 name 汇总值的 dict
reset_on_submit (form)True / FalseFalse提交后清空输入
no_validate (form)True / FalseFalse关闭浏览器的 required·pattern 检查
html_for (label, output)元素 id无要关联的输入框。点标签就会跳到那个输入框
disabled (fieldset, select, option, optgroup)True / FalseFalse禁用内部所有输入
value / on_change (select, textarea)字符串 / 事件无受控输入。default_value 是非受控
multiple size (select)bool / 整数False / 无多选、显示的行数
label (optgroup)字符串无分组标题
rows cols max_length (textarea)整数无尺寸与字数限制
auto_height enter_key_submit (textarea)True / FalseFalse随文字增高、按 Enter 提交
value min max low high optimum (meter)数字min=0 max=1范围内的测量值
value max (progress)数字max=1进度。没有 value 时显示为进行中

rx.el.form 与 on_submit

点击提交按钮后,on_submit 会把表单内输入框的值以 name 为键组成 dict 传入。复选框只在勾选时以 "on" 进入。 即使没有 on_submit,rx.el.form 也会阻止浏览器的默认提交(重新加载页面)。reset_on_submit=True 时,提交后清空输入框。

    class HtmlFormSignupState(rx.State):
        submitted: dict[str, str] = {}
    
        @rx.event
        def handle_submit(self, form_data: dict):
            self.submitted = {key: str(value) for key, value in form_data.items()}
    
    
    rx.el.div(
        rx.el.form(
            rx.el.input(name="email", type="email", placeholder="Email", required=True, border="1px solid gray", padding="4px 8px"),
            rx.el.select(
                rx.el.option("Weekly", value="weekly"),
                rx.el.option("Monthly", value="monthly"),
                name="plan",
                border="1px solid gray",
                padding="4px",
            ),
            rx.el.label(rx.el.input(type="checkbox", name="news"), " Send news"),
            rx.el.button("Sign up", type="submit", padding="4px 12px", border="1px solid gray", border_radius="6px"),
            on_submit=HtmlFormSignupState.handle_submit,
            reset_on_submit=True,
            display="flex",
            gap="8px",
            flex_wrap="wrap",
            align_items="center",
        ),
        rx.el.ul(
            rx.foreach(HtmlFormSignupState.submitted, lambda kv: rx.el.li(rx.el.code(kv[0]), " = ", kv[1])),
            margin_top="10px",
        ),
    )

    fieldset、legend、label

    fieldset 用边框把相关的输入框起来,legend 是这一组的标题。给 fieldset 设置 disabled=True,里面的输入就会全部禁用。 label 可以包住输入框,也可以在 html_for 里写上输入框的 id 来关联。点击关联的标签,光标就会移到输入框里。

    Delivery
    Gift wrap (sold out)
    rx.el.div(
        rx.el.fieldset(
            rx.el.legend("Delivery", font_weight="bold"),
            rx.el.label(rx.el.input(type="radio", name="ship", value="pickup", default_checked=True), " Pickup"),
            rx.el.label(rx.el.input(type="radio", name="ship", value="courier"), " Courier"),
            border="1px solid gray",
            border_radius="6px",
            padding="8px 12px",
            display="flex",
            gap="12px",
        ),
        rx.el.fieldset(
            rx.el.legend("Gift wrap (sold out)", font_weight="bold"),
            rx.el.label(rx.el.input(type="checkbox"), " Wrap it"),
            rx.el.label("Message ", rx.el.input(id="htmlforms_gift_msg", border="1px solid gray")),
            disabled=True,
            border="1px solid gray",
            border_radius="6px",
            padding="8px 12px",
            display="flex",
            gap="12px",
            opacity="0.6",
        ),
        rx.el.label("Coupon", html_for="htmlforms_coupon", margin_right="8px"),
        rx.el.input(id="htmlforms_coupon", placeholder="Click the label", border="1px solid gray", padding="2px 6px"),
        display="flex",
        flex_direction="column",
        gap="10px",
        align_items="start",
    )

    select、optgroup、option

    option 是可选的项目,optgroup 把项目归到 label 标题下。disabled 的项目不能选。 用 value 和 on_change 让 State 掌管选中的值。给 multiple=True 和 size,就能在展开的列表中用 Ctrl·Shift 多选。

    You picked: soup

    class HtmlFormSelectState(rx.State):
        dish: str = "soup"
    
        @rx.event
        def change_dish(self, value: str):
            self.dish = value
    
    
    rx.el.div(
        rx.el.select(
            rx.el.optgroup(
                rx.el.option("Soup", value="soup"),
                rx.el.option("Salad", value="salad"),
                label="Starters",
            ),
            rx.el.optgroup(
                rx.el.option("Pasta", value="pasta"),
                rx.el.option("Steak (sold out)", value="steak", disabled=True),
                label="Mains",
            ),
            value=HtmlFormSelectState.dish,
            on_change=HtmlFormSelectState.change_dish,
            border="1px solid gray",
            padding="4px",
        ),
        rx.el.p("You picked: ", rx.el.strong(HtmlFormSelectState.dish)),
        rx.el.select(
            rx.el.option("Red"),
            rx.el.option("Green"),
            rx.el.option("Blue"),
            rx.el.option("Black"),
            multiple=True,
            size=3,
            border="1px solid gray",
            width="120px",
        ),
        display="flex",
        flex_direction="column",
        gap="8px",
        align_items="start",
    )

    textarea:按 Enter 发送与自动高度

    Reflex 的 rx.el.textarea 有两个 HTML 里没有的属性。enter_key_submit=True 时,Enter 提交表单,Shift+Enter 换行。 auto_height=True 时,文字越长高度越高(配合 min_height 使用)。enter_key_submit 不能与 on_key_down 同时使用。

      class HtmlFormChatState(rx.State):
          messages: list[str] = []
      
          @rx.event
          def send(self, form_data: dict):
              text = str(form_data.get("message", "")).strip()
              if text:
                  self.messages = [*self.messages, text][-4:]
      
      
      rx.el.div(
          rx.el.ul(
              rx.foreach(HtmlFormChatState.messages, lambda m: rx.el.li(m, white_space="pre-wrap")),
              list_style_type="disc",
              padding_left="1.5em",
          ),
          rx.el.form(
              rx.el.textarea(
                  name="message",
                  placeholder="Type and press Enter",
                  enter_key_submit=True,
                  auto_height=True,
                  max_length=200,
                  min_height="40px",
                  width="100%",
                  border="1px solid gray",
                  border_radius="6px",
                  padding="6px 8px",
              ),
              on_submit=HtmlFormChatState.send,
              reset_on_submit=True,
          ),
          max_width="360px",
      )

      output、meter、progress

      output 是显示计算结果的位置,在 html_for 里用空格隔开写上参与计算的输入框 id。 meter 是磁盘用量这类有固定范围的测量值,progress 是任务的进度。不给 progress 设置 value,就会显示为“进行中”。

      + = 5

      class HtmlFormOutputState(rx.State):
          a: int = 2
          b: int = 3
      
          @rx.event
          def change_a(self, value: float):
              self.a = int(value)
      
          @rx.event
          def change_b(self, value: float):
              self.b = int(value)
      
          @rx.var
          def total(self) -> int:
              return self.a + self.b
      
      
      rx.el.div(
          rx.el.p(
              rx.el.input(id="htmlforms_a", type="number", value=HtmlFormOutputState.a, on_change=HtmlFormOutputState.change_a, width="60px", border="1px solid gray"),
              " + ",
              rx.el.input(id="htmlforms_b", type="number", value=HtmlFormOutputState.b, on_change=HtmlFormOutputState.change_b, width="60px", border="1px solid gray"),
              " = ",
              rx.el.output(HtmlFormOutputState.total, html_for="htmlforms_a htmlforms_b", name="total", font_weight="bold"),
          ),
          rx.el.label("Disk ", rx.el.meter(value=0.7, low=0.5, high=0.8, optimum=0.2, width="160px")),
          rx.el.label("Loading ", rx.el.progress(width="160px")),
          display="flex",
          flex_direction="column",
          gap="8px",
      )

      完成订单

      饮品用 optgroup 分组,数量用数字输入,取餐方式是 fieldset 里的单选按钮,备注用 textarea。提交后,State 把摘要写进 output。

      Your order
      Pickup
      class HtmlFormOrderState(rx.State):
          summary: str = ""
      
          @rx.event
          def place_order(self, form_data: dict):
              qty = form_data.get("qty") or "1"
              note = str(form_data.get("note", "")).strip() or "none"
              self.summary = f"{qty} x {form_data.get('drink')} ({form_data.get('size')}), {form_data.get('pickup')}. Note: {note}"
      
      
      rx.el.div(
          rx.el.form(
              rx.el.fieldset(
                  rx.el.legend("Your order", font_weight="bold"),
                  rx.el.select(
                      rx.el.optgroup(rx.el.option("Latte"), rx.el.option("Mocha"), label="Coffee"),
                      rx.el.optgroup(rx.el.option("Green tea"), rx.el.option("Chai"), label="Tea"),
                      name="drink",
                      border="1px solid gray",
                      padding="4px",
                  ),
                  rx.el.select(rx.el.option("Small"), rx.el.option("Large"), name="size", border="1px solid gray", padding="4px"),
                  rx.el.input(name="qty", type="number", min=1, max=9, default_value=1, width="60px", border="1px solid gray", padding="4px"),
                  display="flex",
                  gap="8px",
                  flex_wrap="wrap",
                  border="1px solid gray",
                  border_radius="6px",
                  padding="8px 12px",
              ),
              rx.el.fieldset(
                  rx.el.legend("Pickup", font_weight="bold"),
                  rx.el.label(rx.el.input(type="radio", name="pickup", value="for here", default_checked=True), " For here"),
                  rx.el.label(rx.el.input(type="radio", name="pickup", value="to go"), " To go"),
                  display="flex",
                  gap="12px",
                  border="1px solid gray",
                  border_radius="6px",
                  padding="8px 12px",
              ),
              rx.el.textarea(name="note", placeholder="Notes for the barista", rows=2, border="1px solid gray", border_radius="6px", padding="6px 8px"),
              rx.el.button("Order", type="submit", padding="6px 14px", border="1px solid gray", border_radius="6px", background=rx.color("indigo", 3)),
              on_submit=HtmlFormOrderState.place_order,
              display="flex",
              flex_direction="column",
              gap="10px",
          ),
          rx.el.output(HtmlFormOrderState.summary, display="block", margin_top="10px", font_weight="600"),
          max_width="420px",
      )

      工坊笔记

      • rx.el.form 的 on_submit 接收 {name: 值} dict。每个输入框都要加 name。

      • 用 fieldset + legend 分组,用 fieldset 的 disabled 一次性禁用。label 通过 html_for 关联到 id。

      • select 用 option·optgroup 组织,用 value + on_change 受控,用 multiple·size 多选。

      • textarea 的 enter_key_submit·auto_height 是 Reflex 额外提供的便利属性。

      • meter 是按 low·high·optimum 变色的测量值,progress 是进度,output 是计算结果。

      ← 上一课

      HTML 元素 · HTML Layout

      下一课 →

      HTML 元素 · HTML Media

      订阅新课时和新课程通知

      本课程新增课时或推出后续课程时,我们会发邮件通知你。

      收集项目:电子邮箱 · 用途:本课程的新课时和上线通知 · 保存期限:直到你要求删除。不会用于其他用途,也不会提供给他人。回复任意通知邮件,我们会立即删除。

      问答

      关于本课有任何疑问都可以在这里留言。所有语言版本页面的提问会汇集在一起。

      以纯文本显示,不支持链接和格式。

      R

      ReflexKR

      动手学 Python 和 Reflex。所有示例均已在 Reflex 0.10.0 上实际运行验证。

      ReflexKR 免费,没有横幅广告。如果对你有帮助,欢迎请我们喝杯咖啡。 ☕ 请我们喝杯咖啡

      联盟链接与赞助说明
      한국어English日本語简体中文EspañolDeutsch

      © 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

      Built with Reflex