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
rx.el.arearx.el.audiorx.el.canvasrx.el.embedrx.el.iframerx.el.imgrx.el.mathrx.el.metarx.el.objectrx.el.picturerx.el.sourcerx.el.trackrx.el.videoOfficial docs
Workshop order
A product detail screen: a large photo (a square photo on narrow screens), a how-to video and a sound preview, using only HTML media elements.
These HTML elements put images, video, sound and other documents on a page. img, video and audio are the most common; picture and source pick the right file for the screen; and iframe, embed and object embed other documents.
They are simpler than Reflex's rx.image, rx.video and rx.audio and use the browser's built-in features as they are.
rx.el.img(src="https://picsum.photos/id/1025/300/200", alt="A dog in a blanket", width="300px", height="200px")For img, change how it fits its box (object_fit) and the CSS filter; for video, change the control bar (controls), looping and muting.
Try the props
width
fit
radius
filter
loading
rx.el.img(
src="https://picsum.photos/id/1043/400/250",
alt="A city street",
width="240px",
height="160px",
object_fit="cover",
border_radius="8px",
filter="none",
loading="lazy",
background=rx.color("gray", 4),
)Try the props
controls
loop
muted
width
rx.el.video(
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
poster="https://picsum.photos/id/152/320/180",
controls=True,
loop=False,
muted=True,
plays_inline=True,
width="280px",
)| Prop | Values | Default | Use |
|---|---|---|---|
src alt (img) | URL / string | none | Image address and alternative text. Always write alt |
src_set sizes (img, source) | list of candidates | none | Pick a file for the screen density and width |
loading (img, iframe) | "lazy" "eager" | "eager" | Load when it gets close to the viewport |
decoding (img) | "async" "sync" "auto" | "auto" | When the image is decoded |
controls loop muted auto_play (video, audio) | True / False | False | Control bar, looping, muting, autoplay |
poster plays_inline (video) | URL / bool | none | Image before playback; play on mobile without going full screen |
preload (video, audio) | "none" "metadata" "auto" | varies by browser | How much to download in advance |
media type (source) | media query / MIME | none | Condition and file format |
kind src_lang label default (track) | string, bool | none | Subtitle file |
src_doc sandbox allow (iframe) | HTML / list of permissions | none | Document to embed and its restrictions |
shape coords href (area) | "rect" "circle" "poly" … | none | Clickable regions of an image map |
alt is shown in place of the image when it can't load, and it's the text screen readers read. The image on the right deliberately uses an address that doesn't exist on this site, so its alt shows.
Give src_set 1x and 2x candidates and high-resolution screens pick the larger file. loading="lazy" defers loading until the image gets close to the viewport.

rx.el.div(
rx.el.img(
src="https://picsum.photos/id/1025/150/100",
src_set="https://picsum.photos/id/1025/150/100 1x, https://picsum.photos/id/1025/300/200 2x",
alt="Sharp on high density screens",
loading="lazy",
decoding="async",
),
rx.el.img(src="/no-such-image.png", alt="Broken image: the alt text shows"),
display="flex",
gap="12px",
align_items="center",
)The browser goes through the sources inside picture from the top and uses the first file whose media condition matches. If none match, it uses the final img.
Make the window narrower than 640px and it switches to the square photo.
rx.el.picture(
rx.el.source(media="(max-width: 640px)", src_set="https://picsum.photos/id/1015/300/300"),
rx.el.img(src="https://picsum.photos/id/1016/480/200", alt="A landscape", max_width="100%"),
)You can pass src directly, or give several files in different formats as source children. The browser picks the first source it can play.
Text after the sources only shows in browsers that can't play the video. Most browsers only allow autoplay (auto_play) when the media is muted.
rx.el.div(
rx.el.video(
rx.el.source(src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4", type="video/mp4"),
"Your browser cannot play this video.",
controls=True,
muted=True,
preload="metadata",
width="280px",
),
rx.el.audio(
src="https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3",
controls=True,
preload="none",
),
display="flex",
flex_direction="column",
gap="12px",
)track attaches a WebVTT (.vtt) subtitle file. The subtitle file has to be on the same site, so here it is shown as code only. Put the file in the assets folder.
rx.el.video(
rx.el.source(src="/flower.mp4", type="video/mp4"),
rx.el.track(src="/flower.en.vtt", kind="subtitles", src_lang="en", label="English", default=True),
controls=True,
)Give src the address of another page, or pass HTML directly in src_doc. sandbox="" blocks all scripts, forms and pop-ups in the embedded document; lift only what you need, such as "allow-scripts".
title tells screen readers what's inside. Many other sites block being embedded.
rx.el.iframe(
src_doc="<h3 style='font-family:sans-serif'>Hello from an iframe</h3><p style='font-family:sans-serif'>This page lives in src_doc.</p>",
sandbox="",
loading="lazy",
title="Inline document",
width="320px",
height="130px",
border="1px solid gray",
border_radius="6px",
)Both are old ways of including external resources. embed only takes src and type; object takes data and shows its children instead if loading fails.
Nowadays it's better to use img for images, video for video and iframe for documents.
rx.el.div(
rx.el.embed(src="https://picsum.photos/id/237/160/110", type="image/jpeg", width="160px", height="110px"),
rx.el.object(
"Fallback text if the data cannot load",
data="https://picsum.photos/id/40/160/110",
type="image/jpeg",
width="160px",
height="110px",
),
display="flex",
gap="12px",
)canvas is an empty drawing board, and the drawing is done by JavaScript. Below, rx.call_script in on_mount draws a rectangle and a circle.
A canvas's width and height aren't CSS sizes but the board's pixel count, so in Reflex they are passed through custom_attrs (as keywords they would become CSS).
rx.el.canvas(
id="htmlmedia_canvas",
custom_attrs={"width": "260", "height": "100"},
border="1px solid gray",
border_radius="6px",
on_mount=rx.call_script(
"const g = document.getElementById('htmlmedia_canvas').getContext('2d');"
" g.fillStyle = '#6e56cf'; g.fillRect(20, 20, 120, 60);"
" g.fillStyle = '#e54d2e'; g.beginPath(); g.arc(200, 50, 30, 0, Math.PI * 2); g.fill();"
),
)The areas inside a map are the clickable regions on an image. Connect it by writing "#map-name" in the img's use_map. The left half of the photo and the circle on the right each lead to a different document.
rx.el.div(
rx.el.map(
rx.el.area(shape="rect", coords="0,0,150,200", href="https://reflex.dev/docs/library/", target="_blank", alt="Library docs"),
rx.el.area(shape="circle", coords="225,100,60", href="https://reflex.dev/docs/library/html/media/", target="_blank", alt="Media docs"),
name="htmlmedia-map",
),
rx.el.img(src="https://picsum.photos/id/1060/300/200", alt="Image map", use_map="#htmlmedia-map", width="300px", height="200px"),
)rx.el.math is a container for formulas (MathML), but the tags that go inside it, such as mi, mo and msup, aren't in rx.el. For formulas, it's easier to insert the whole MathML with rx.html.
meta is information for the document head. React 19 hoists meta from the body into the head, so it isn't rendered on the page; you usually pass it through the meta argument of @rx.page.
rx.html("<math display='block'><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup><mo>=</mo><msup><mi>c</mi><mn>2</mn></msup></math>")rx.el.math(...) # MathML box; rx.el has no mi/mo/mn tags to put inside it
rx.el.meta(name="description", content="A page about media") # for the document head
@rx.page(title="Media", description="A page about media", meta=[{"name": "theme-color", "content": "#6e56cf"}])
def media_page(): ...The photo adapts to the screen with picture, the video isn't downloaded until clicked thanks to poster and preload="none", and the sound uses audio's default bar.
See it in action
Sound preview
rx.el.div(
rx.el.picture(
rx.el.source(media="(max-width: 640px)", src_set="https://picsum.photos/id/26/320/320"),
rx.el.img(src="https://picsum.photos/id/26/440/240", alt="Travel kit on a table", border_radius="8px", max_width="100%"),
),
rx.el.p("See it in action", font_weight="600", margin_top="8px"),
rx.el.video(
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
poster="https://picsum.photos/id/26/440/248",
controls=True,
muted=True,
plays_inline=True,
preload="none",
width="100%",
),
rx.el.p("Sound preview", font_weight="600", margin_top="8px"),
rx.el.audio(src="https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3", controls=True, preload="none"),
max_width="440px",
)Workshop notes
Always give img an alt. src_set and loading="lazy" fetch the right file, later.
picture + source (media) shows different images depending on the screen, with an img last.
video and audio get a bar with controls; autoplay only together with muted. Subtitles go in track.
iframe takes src or src_doc and is restricted with sandbox. embed and object are old methods.
Set canvas size with custom_attrs and draw with JavaScript. Pass meta with @rx.page(meta=...).
Get news about new lessons
We'll email you when this course gets new lessons or a follow-up course opens.
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© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Questions & answers
Ask anything about this lesson. Questions from every language version of this page appear together.