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
Other tools

Html Embed

Workshop order

Show the store notices that come down from head office as HTML snippets exactly as they are, without editing them, but still looking good.

rx.html takes a string of HTML code and renders it as is. Use it to show fragments that were already produced as HTML elsewhere (a CMS, an email body, a Markdown converter). When you build the screen yourself, the official docs also recommend rx.el.* elements over rx.html, because you can attach events and State to them.

Hello from raw HTML.

rx.html("<p>Hello from <b>raw</b> <i>HTML</i>.</p>")

Try the props

rx.html creates a single <div> and puts the HTML inside it. So styles such as font_size and color go on the outer div, and the text inside inherits them.

Try the props

Bold and italic

snippet

size

color

rx.html("<b>Bold</b> and <i>italic</i>", font_size="18px", color="inherit")
Change the HTML snippet and the styles of the outer div. The ul's bullets don't show because of this site's preflight.
PropValuesDefaultUse
first childHTML string or string VarrequiredThe HTML to render. Takes exactly one
class_nameclass string"rx-Html" is always addedClass of the outer div
style props, styleCSS, nested selectors such as "& h2"noneStyles for the outer div and the elements inside
dangerouslySetInnerHTML{"__html": ...}set automatically from the childInternal implementation. You never set it yourself
common attributesid title role …noneHTML attributes of the div

Five headings, one look

This is the example from the official docs. On this site all five headings look the same size, because Radix and Tailwind have wiped out the default heading styles.

Hello World

Hello World

Hello World

Hello World
Hello World
rx.vstack(
    rx.html("<h2>Hello World</h2>"),
    rx.html("<h3>Hello World</h3>"),
    rx.html("<h4>Hello World</h4>"),
    rx.html("<h5>Hello World</h5>"),
    rx.html("<h6>Hello World</h6>"),
)

If the styles are gone

The official docs recommend class_name="prose" with Tailwind's @tailwindcss/typography plugin. This site doesn't have that plugin, so it's shown as code only.

# The official docs' way: the prose class from the Tailwind typography plugin
rx.html("<h2>Hello World</h2>", class_name="prose")
# + enable the @tailwindcss/typography plugin in rxconfig.py's Tailwind settings (not on this site)

Even without the plugin, nested selectors such as "& h2" in style let you style each element of the inner HTML.

Hello World

Hello World

Hello World

rx.html(
    "<h2>Hello World</h2><h3>Hello World</h3><h4>Hello World</h4>",
    style={
        "& h2": {"font_size": "24px", "font_weight": "bold"},
        "& h3": {"font_size": "20px", "font_weight": "bold"},
        "& h4": {"font_size": "16px", "font_weight": "600", "color": rx.color("gray", 11)},
    },
)

Adding images

<img> tags go in as they are too. The official docs use the Reflex banner image, but this course uses picsum images for all external resources.

A waterfall
rx.html(
    "<img src='https://picsum.photos/id/1039/400/160' alt='A waterfall' style='border-radius: 8px' />"
)

HTML built from State, and escaping

Give a string Var as the child and the HTML changes whenever the State does. In that case, never insert text the user typed as is. Convert < and > with html.escape and they show up as characters rather than tags. Check that the <b> in the input below shows as literal text instead of turning bold. Browsers don't run <script> tags inserted by rx.html even without escaping, but attributes such as onerror do run, so escaping is necessary.

Welcome, <b>Ada</b>!

<p>Welcome, <mark>&lt;b&gt;Ada&lt;/b&gt;</mark>!</p>
import html


class HtmlEmbedPreviewState(rx.State):
    name: str = "<b>Ada</b>"

    @rx.event
    def change_name(self, value: str):
        self.name = value

    @rx.var
    def card_html(self) -> str:
        safe = html.escape(self.name)
        return f"<p>Welcome, <mark>{safe}</mark>!</p>"


rx.vstack(
    rx.input(value=HtmlEmbedPreviewState.name, on_change=HtmlEmbedPreviewState.change_name, width="100%"),
    rx.html(HtmlEmbedPreviewState.card_html),
    rx.code(HtmlEmbedPreviewState.card_html, size="1"),
    width="100%",
    max_width="420px",
)

Writing it with rx.el

If you can write the content yourself, it's better to build the same screen from rx.el elements. You can attach on_click and State to each element, and Reflex handles escaping for you.

Hello from raw HTML.

Hello from rx.el elements.

rx.flex(
    rx.html("<p>Hello from <b>raw</b> <i>HTML</i>.</p>"),
    rx.el.p("Hello from ", rx.el.b("rx.el"), " ", rx.el.i("elements"), "."),
    direction="column",
    spacing="2",
)

Filling the order

Not a single character of the notice HTML is changed; from the outside, the "& h3", "& ul" and "& a" selectors only add the heading size, list bullets and link color.

Store hours change

From Nov 1 we open at 8 am.

  • Mon to Fri: 8 am - 8 pm
  • Sat: 9 am - 5 pm

Read more

rx.card(
    rx.html(
        "<h3>Store hours change</h3>"
        "<p>From <b>Nov 1</b> we open at <mark>8 am</mark>.</p>"
        "<ul><li>Mon to Fri: 8 am - 8 pm</li><li>Sat: 9 am - 5 pm</li></ul>"
        "<p><a href='https://reflex.dev/docs/library/other/html-embed/' target='_blank'>Read more</a></p>",
        style={
            "& h3": {"font_size": "18px", "font_weight": "bold", "margin_bottom": "6px"},
            "& ul": {"list_style_type": "disc", "padding_left": "1.4em", "margin_y": "6px"},
            "& a": {"color": rx.color("indigo", 11), "text_decoration": "underline"},
        },
    ),
    max_width="420px",
)

Workshop notes

  • rx.html("<p>...</p>") puts an HTML string as is inside a single div. Exactly one child.

  • For screens you build yourself, rx.el.* elements are better. rx.html is for fragments that are already HTML.

  • Default heading and list styles are wiped. Add them with the prose class or nested selectors such as "& h2".

  • Escape user input with html.escape before inserting it.

← Previous

Other tools · Clipboard

Next →

Other tools · Script

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