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.skeletonOfficial docs
Workshop order
For the 1–2 seconds the profile card takes to load, show gray placeholders of the same size so the screen doesn't jump.
rx.skeleton is a gray placeholder shown in place of content while it loads. Wrap a child and it becomes a pulsing box the size of that child;
when loading finishes, the real content appears in the same spot, so the layout doesn't shift. Without loading, it shows the placeholder.
rx.skeleton(rx.button("Loading button"))Turn off loading and the placeholder lifts to reveal the real content. An rx.skeleton without children gets its size from width and height.
Try the props
loading
title
width
height
rx.vstack(
rx.flex(
rx.skeleton(rx.avatar(fallback="RX", size="4"), loading=True),
rx.box(
rx.skeleton(rx.heading("Grace Hopper", size="3"), loading=True),
rx.skeleton(rx.text("Posted two days ago.", size="2"), loading=True),
),
rx.skeleton(rx.button("Follow"), loading=True),
spacing="3",
align="center",
),
rx.skeleton(width="240px", height="24px", loading=True),
spacing="4",
)| Prop | Values | Default | Use |
|---|---|---|---|
loading | True / False | True | Whether to show the placeholder or the real content |
width height | CSS length (responsive allowed) | size of the child | Size of the placeholder |
min_width max_width | CSS length | none | Minimum / maximum width |
min_height max_height | CSS length | none | Minimum / maximum height |
This is the example from the official docs. The two placeholders wrapping buttons have their height changed with height, and the two below show loading set to True and to False.
Text is already loaded.
rx.vstack(
rx.skeleton(rx.button("button-small"), height="10px"),
rx.skeleton(rx.button("button-big"), height="20px"),
rx.skeleton(rx.text("Text is loaded."), loading=True),
rx.skeleton(rx.text("Text is already loaded."), loading=False),
)A placeholder for text wraps the text itself, not the parent around it. Wrap the parent and its full width turns gray;
wrap the text and only the actual length of the text turns gray. Write it as rx.text(rx.skeleton("...")).
Wrap the parent:
Wrap the text itself:
rx.vstack(
rx.text("Wrap the parent:", size="1", color_scheme="gray"),
rx.skeleton(rx.box(rx.text("Short text"), width="100%")),
rx.text("Wrap the text itself:", size="1", color_scheme="gray"),
rx.text(rx.skeleton("Short text")),
width="300px",
)With no children and just width and height, you get an empty placeholder box. Use it to reserve space for lines, images or icons when you don't yet know what content will go there.
rx.vstack(
rx.skeleton(width="240px", height="16px"),
rx.skeleton(width="180px", height="16px"),
rx.skeleton(width="100%", max_width="320px", height="80px"),
rx.skeleton(width="48px", height="48px"),
width="100%",
)Click Reload and a background event turns loading on, waits 1.5 seconds and turns it off. Meanwhile the avatar, name and bio each turn into placeholders of their own size.
The bio wraps the text inside rx.text, so only the length of the text turns gray.
Wrote the first program.
import asyncio
class SkeletonProfileState(rx.State):
loading: bool = False
name: str = "Ada Lovelace"
bio: str = "Wrote the first program."
@rx.event(background=True)
async def reload_profile(self):
async with self:
self.loading = True
await asyncio.sleep(1.5)
async with self:
self.loading = False
rx.card(
rx.flex(
rx.skeleton(rx.avatar(fallback="AL", size="5", radius="full"), loading=SkeletonProfileState.loading),
rx.box(
rx.skeleton(rx.heading(SkeletonProfileState.name, size="4"), loading=SkeletonProfileState.loading),
rx.text(rx.skeleton(SkeletonProfileState.bio, loading=SkeletonProfileState.loading), size="2", color_scheme="gray"),
),
spacing="4",
align="center",
),
rx.button(
"Reload",
on_click=SkeletonProfileState.reload_profile,
disabled=SkeletonProfileState.loading,
variant="soft",
margin_top="12px",
),
max_width="360px",
)Workshop notes
rx.skeleton(child) becomes a gray placeholder of exactly the child's size.
loading=False shows the real content. If not given, it shows the placeholder (True).
Wrap the text itself, not its parent: rx.text(rx.skeleton("...")).
Without children, reserve empty space with width and height.
While the placeholder shows, buttons and inputs inside can't be clicked.
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.