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.plotlyOfficial docs
Workshop order
Turn a year of sales per product into an interactive chart you can zoom in and out of, and make the chart change when a product is picked.
Plotly is a Python library for interactive charts you can zoom, pan and hover over. rx.plotly takes a Plotly Figure as is and draws it in the browser. From basic line and bar charts to heatmaps, 3D and financial charts, you use it all without JavaScript.
Instead of calling fig.show() as in a normal script, pass the figure in your UI code with rx.plotly(data=fig). The package you need is plotly, and this course builds figures with plotly.graph_objects (go).
import plotly.graph_objects as go
plotly_years = [1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007]
plotly_canada = [68.75, 69.96, 71.3, 72.13, 72.88, 74.21, 75.76, 76.86, 77.95, 78.61, 79.77, 80.65]
plotly_line_fig = go.Figure(go.Scatter(x=plotly_years, y=plotly_canada, mode="lines", name="Canada"))
plotly_line_fig.update_layout(title="Life expectancy in Canada")
rx.plotly(data=plotly_line_fig, width="100%")Most examples in the official docs use plotly.express (px). px needs pandas, so this page builds the same figures with go and writes the data as Python lists. A figure made with px is also a Figure, so you pass it to rx.plotly the same way.
Give layout a dict and it is written over the figure's layout. That is handy for changing the title, height or background on the screen side while keeping the same figure. The lab below and several examples reuse the plotly_line_fig from above.
Try the props
title
height
400
showlegend
plot_bgcolor
locale
rx.plotly(
data=plotly_line_fig,
layout={"title": {"text": "Life expectancy"}, "height": 400, "showlegend": False, "plot_bgcolor": "rgba(0,0,0,0)"},
locale="en",
width="100%",
)| Prop | Values | Default | Use |
|---|---|---|---|
data | go.Figure or its dict | - | The figure to draw. A State value works too |
layout | dict | - | Values written over the figure's layout |
template | Plotly Template | plotly in light mode, plotly_dark in dark mode | Overall color and font theme |
config | dict | - | Plotly settings such as the toolbar and scroll zoom |
locale | "de" "ko" … | - | Language for number and date formats and toolbar text |
use_resize_handler | True / False | True | Refits when the window size changes |
on_click · on_hover · on_unhover · on_selected · on_selecting | events | - | Pass a list of points (list[dict]) |
on_relayout · on_double_click · on_deselect · on_after_plot etc. | events | - | Zoom or pan, double-click, deselect, after a redraw (no arguments) |
Without a template, Reflex switches the theme to match light and dark mode. rx.plotly in Reflex 0.10 also has on_click and on_double_click, which are missing from the official docs table.
These are the Plotly Express examples from the official docs, ported to go. Whatever the figure, you pass it the same way: rx.plotly(data=figure). The data is made up for the examples.
Add several go.Bar traces and line them up side by side with barmode="group". This matches px.bar(..., color="country").
import plotly.graph_objects as go
plotly_bar_fig = go.Figure([
go.Bar(name="Chairs", x=["Q1", "Q2", "Q3", "Q4"], y=[120, 150, 170, 210]),
go.Bar(name="Tables", x=["Q1", "Q2", "Q3", "Q4"], y=[80, 95, 130, 120]),
])
plotly_bar_fig.update_layout(barmode="group", title="Orders by quarter")
rx.plotly(data=plotly_bar_fig, width="100%")Give go.Scatter mode="markers" and it draws only points. One trace per group gives each group its own color and legend entry.
import random
import plotly.graph_objects as go
plotly_scatter_rng = random.Random(7)
plotly_scatter_fig = go.Figure([
go.Scatter(
x=[plotly_scatter_rng.gauss(cx, 0.3) for _ in range(30)],
y=[plotly_scatter_rng.gauss(cy, 0.4) for _ in range(30)],
mode="markers",
name=name,
)
for name, cx, cy in [("A", 3.4, 5.0), ("B", 2.8, 5.9), ("C", 3.0, 6.6)]
])
plotly_scatter_fig.update_layout(title="Sample clusters", xaxis_title="width", yaxis_title="length")
rx.plotly(data=plotly_scatter_fig, width="100%")Give go.Pie the names (labels) and values (values). Plotly works out the proportions.
import plotly.graph_objects as go
plotly_pie_fig = go.Figure(go.Pie(labels=["Thur", "Fri", "Sat", "Sun"], values=[171.83, 51.96, 260.40, 247.39]))
plotly_pie_fig.update_layout(title="Tips by day")
rx.plotly(data=plotly_pie_fig, width="100%")go.Histogram2d shades a cell darker the more pairs of values fall into it (px.density_heatmap). If you already have a computed table, use go.Heatmap(z=...).
import random
import plotly.graph_objects as go
plotly_heat_rng = random.Random(3)
plotly_bills = [plotly_heat_rng.gauss(20, 8) for _ in range(300)]
plotly_tips = [b * 0.15 + plotly_heat_rng.gauss(0, 0.8) for b in plotly_bills]
plotly_heatmap_fig = go.Figure(go.Histogram2d(x=plotly_bills, y=plotly_tips, nbinsx=20, nbinsy=15))
plotly_heatmap_fig.update_layout(title="Bill vs tip density heatmap")
rx.plotly(data=plotly_heatmap_fig, width="100%")go.Histogram splits values into bins and counts them. nbinsx is the upper limit on the number of bins.
import random
import plotly.graph_objects as go
plotly_hist_rng = random.Random(5)
plotly_hist_fig = go.Figure(go.Histogram(x=[plotly_hist_rng.gauss(20, 8) for _ in range(250)], nbinsx=20))
plotly_hist_fig.update_layout(title="Distribution of total bills")
rx.plotly(data=plotly_hist_fig, width="100%")go.Box shows the median, quartiles and outliers in one box. Use one trace per group.
import random
import plotly.graph_objects as go
plotly_box_rng = random.Random(11)
plotly_box_fig = go.Figure([
go.Box(y=[plotly_box_rng.gauss(mean, 6) for _ in range(40)], name=day)
for day, mean in [("Thur", 17), ("Fri", 17), ("Sat", 20), ("Sun", 21)]
])
plotly_box_fig.update_layout(title="Total bill by day")
rx.plotly(data=plotly_box_fig, width="100%")A scatter plot that shows a third value through the point size (marker.size). Switch the x axis to type="log" and it matches px.scatter(..., log_x=True).
import plotly.graph_objects as go
plotly_bubble_fig = go.Figure(go.Scatter(
x=[800, 2500, 6000, 15000, 32000, 45000],
y=[52, 61, 68, 74, 79, 82],
text=["A", "B", "C", "D", "E", "F"],
mode="markers",
marker={"size": [20, 45, 30, 60, 25, 40], "color": [1, 2, 3, 4, 5, 6], "colorscale": "Viridis"},
))
plotly_bubble_fig.update_xaxes(type="log", title="income")
plotly_bubble_fig.update_layout(title="Income vs life expectancy", yaxis_title="years")
rx.plotly(data=plotly_bubble_fig, width="100%")A schedule is drawn with horizontal bars (orientation="h"). Each bar starts at base, its length is the duration in milliseconds, and the x axis is a date axis. This is what px.timeline does internally. The y axis is reversed so it reads from top to bottom.
from datetime import date
import plotly.graph_objects as go
plotly_jobs = [("Job A", "2009-01-01", "2009-02-28"), ("Job B", "2009-03-05", "2009-04-15"), ("Job C", "2009-02-20", "2009-05-30")]
plotly_gantt_fig = go.Figure(go.Bar(
y=[job for job, start, end in plotly_jobs],
base=[start for job, start, end in plotly_jobs],
x=[(date.fromisoformat(end) - date.fromisoformat(start)).days * 86_400_000 for job, start, end in plotly_jobs],
orientation="h",
))
plotly_gantt_fig.update_xaxes(type="date")
plotly_gantt_fig.update_yaxes(autorange="reversed")
rx.plotly(data=plotly_gantt_fig, width="100%")Spreads hierarchical data from the root in the middle out to the outer rings. Write the parent-child relationships with labels and parents.
import plotly.graph_objects as go
plotly_sunburst_fig = go.Figure(go.Sunburst(
labels=["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
parents=["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve"],
values=[10, 14, 12, 10, 2, 6, 6, 4, 4],
))
rx.plotly(data=plotly_sunburst_fig, width="100%")Shows how much is lost at each stage from visit to purchase. Use it to find the stage where most people drop out.
import plotly.graph_objects as go
plotly_funnel_fig = go.Figure(go.Funnel(
x=[39, 27.4, 20.6, 11, 2],
y=["Website visit", "Downloads", "Potential customers", "Requested price", "Invoice sent"],
))
rx.plotly(data=plotly_funnel_fig, width="100%")locale switches the number and date formats and the toolbar hint text to that language. Below is German. You can pass config as well; when both are set, the locale value is applied last.
rx.plotly(data=plotly_line_fig, locale="de", width="100%")Give go.Surface a two-dimensional list of heights (z) and you get a surface. The official docs read mountain elevation data from a CSV file; here the values come from a formula. Drag to rotate it.
import math
import plotly.graph_objects as go
plotly_surface_z = [[60 + 40 * math.sin(x / 4) * math.cos(y / 5) + y for x in range(30)] for y in range(30)]
plotly_surface_fig = go.Figure(data=[go.Surface(z=plotly_surface_z)])
plotly_surface_fig.update_traces(contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True))
plotly_surface_fig.update_layout(scene_camera_eye=dict(x=1.87, y=0.88, z=-0.64), margin=dict(l=65, r=50, b=65, t=90))
rx.plotly(data=plotly_surface_fig, width="100%")Draws each date's open, high, low and close as one candle. The body spans open to close, the wick low to high.
import plotly.graph_objects as go
plotly_candle_fig = go.Figure(go.Candlestick(
x=["2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05", "2024-01-08", "2024-01-09"],
open=[187.15, 184.22, 182.15, 181.99, 182.09, 183.92],
high=[188.44, 185.88, 183.09, 182.76, 185.60, 185.15],
low=[183.89, 183.43, 180.88, 180.17, 181.50, 182.73],
close=[185.64, 184.25, 181.91, 181.18, 185.56, 185.14],
))
plotly_candle_fig.update_layout(title=dict(text="Stock price"), yaxis=dict(title=dict(text="USD")))
rx.plotly(data=plotly_candle_fig, width="100%")Shows how a starting value changes through the items that add to it and subtract from it. measure sets whether each item is a change ("relative") or a total ("total").
import plotly.graph_objects as go
plotly_waterfall_fig = go.Figure(go.Waterfall(
name="2018",
orientation="v",
measure=["relative", "relative", "total", "relative", "relative", "total"],
x=["Sales", "Consulting", "Net revenue", "Purchases", "Other expenses", "Profit before tax"],
textposition="outside",
text=["+60", "+80", "", "-40", "-20", "Total"],
y=[60, 80, 0, -40, -20, 0],
connector={"line": {"color": "rgb(63, 63, 63)"}},
))
plotly_waterfall_fig.update_layout(title="Profit and loss statement 2018", showlegend=True)
rx.plotly(data=plotly_waterfall_fig, width="100%")A small bar used instead of a gauge. Give go.Indicator gauge.shape="bullet" and fit the actual bar, the ranges (steps) and the target line (threshold) on one line.
import plotly.graph_objects as go
plotly_bullet_fig = go.Figure(go.Indicator(
mode="number+gauge+delta",
value=180,
delta={"reference": 200},
domain={"x": [0.25, 1], "y": [0.4, 0.6]},
title={"text": "Revenue"},
gauge={
"shape": "bullet",
"axis": {"range": [None, 300]},
"threshold": {"line": {"color": "black", "width": 2}, "thickness": 0.75, "value": 170},
"steps": [{"range": [0, 150], "color": "gray"}, {"range": [150, 250], "color": "lightgray"}],
"bar": {"color": "black"},
},
))
plotly_bullet_fig.update_layout(height=250)
rx.plotly(data=plotly_bullet_fig, width="100%")Shows error as a faint band around the line instead of bars. Join the upper bound forward and the lower bound backward into a line that goes all the way around, and fill the inside with fill="toself".
import plotly.graph_objects as go
plotly_band_x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plotly_band_y = [1, 2, 7, 4, 5, 6, 7, 8, 9, 10]
plotly_band_upper = [2, 3, 8, 5, 6, 7, 8, 9, 10, 11]
plotly_band_lower = [0, 1, 5, 3, 4, 5, 6, 7, 8, 9]
plotly_band_fig = go.Figure([
go.Scatter(x=plotly_band_x, y=plotly_band_y, line=dict(color="rgb(0,100,80)"), mode="lines"),
go.Scatter(
x=plotly_band_x + plotly_band_x[::-1],
y=plotly_band_upper + plotly_band_lower[::-1],
fill="toself",
fillcolor="rgba(0,100,80,0.2)",
line=dict(color="rgba(255,255,255,0)"),
hoverinfo="skip",
showlegend=False,
),
])
rx.plotly(data=plotly_band_fig, width="100%")Plotly maps fetch map data from external servers when they draw. Outline maps (Scattergeo, Choropleth) use geography data from the Plotly CDN, and tile maps (Scattermap) use a map tile server. This course does not use external resources, so only the code is shown. In your app they work as written.
An outline map's settings live in the figure's layout.geo, and update_geos adjusts features such as lakes and rivers.
import plotly.graph_objects as go
geo_fig = go.Figure(go.Scattergeo())
geo_fig.update_geos(
visible=False,
resolution=50,
showlakes=True,
lakecolor="Blue",
showrivers=True,
rivercolor="Blue",
)
geo_fig.update_layout(height=300, margin={"r": 0, "t": 0, "l": 0, "b": 0})
def geo_map_chart():
return rx.plotly(data=geo_fig, width="100%")Places points on a tile map. The official docs use px.scatter_map; in go it is go.Scattermap. The map's appearance is set in layout.map.
import plotly.graph_objects as go
map_fig = go.Figure(go.Scattermap(
lat=[37.57, 35.18, 35.87],
lon=[126.98, 129.08, 128.60],
text=["Seoul", "Busan", "Daegu"],
mode="markers",
marker={"size": [18, 12, 10]},
))
map_fig.update_layout(
map={"style": "open-street-map", "center": {"lat": 36.4, "lon": 127.8}, "zoom": 5},
margin={"r": 0, "t": 0, "l": 0, "b": 0},
)
def scatter_map_chart():
return rx.plotly(data=map_fig, width="100%")go.Table takes the column names in header and a list of values per column in cells. It supports scrolling and reordering columns.
import plotly.graph_objects as go
plotly_table_fig = go.Figure(go.Table(
header=dict(values=["A Scores", "B Scores"]),
cells=dict(values=[[100, 90, 80, 90], [95, 85, 75, 95]]),
))
rx.plotly(data=plotly_table_fig, width="100%")Shows the size of flows through the width of the bands. Write the node names in node.label, and in link connect source→target by node index.
import plotly.graph_objects as go
plotly_sankey_fig = go.Figure(go.Sankey(
node=dict(pad=15, thickness=20, line=dict(color="black", width=0.5), label=["A1", "A2", "B1", "B2", "C1", "C2"], color="blue"),
link=dict(source=[0, 1, 0, 2, 3, 3], target=[2, 3, 3, 4, 4, 5], value=[8, 4, 2, 8, 4, 2]),
))
plotly_sankey_fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
rx.plotly(data=plotly_sankey_fig, width="100%")go.Scatter3d plots the relationship of three values in space (px.scatter_3d).
import random
import plotly.graph_objects as go
plotly_s3d_rng = random.Random(2)
plotly_s3d_fig = go.Figure([
go.Scatter3d(
x=[plotly_s3d_rng.gauss(cx, 0.4) for _ in range(30)],
y=[plotly_s3d_rng.gauss(cy, 0.3) for _ in range(30)],
z=[plotly_s3d_rng.gauss(cz, 0.3) for _ in range(30)],
mode="markers",
marker={"size": 4},
name=name,
)
for name, cx, cy, cz in [("A", 5.0, 3.4, 0.2), ("B", 5.9, 2.8, 1.3), ("C", 6.6, 3.0, 2.0)]
])
rx.plotly(data=plotly_s3d_fig, width="100%")In a 3D figure the axes live inside scene. For each axis, as in scene.xaxis, set nticks (number of ticks) and range. The official docs use numpy random numbers; here random does the same job.
import random
import plotly.graph_objects as go
plotly_mesh_rng = random.Random(1)
plotly_mesh_fig = go.Figure(go.Mesh3d(
x=[70 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
y=[55 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
z=[40 * plotly_mesh_rng.gauss(0, 1) for _ in range(70)],
opacity=0.5,
color="rgba(244,22,100,0.6)",
))
plotly_mesh_fig.update_layout(
scene=dict(
xaxis=dict(nticks=4, range=[-100, 100]),
yaxis=dict(nticks=4, range=[-50, 100]),
zaxis=dict(nticks=4, range=[-100, 100]),
),
margin=dict(r=20, l=10, b=10, t=10),
)
rx.plotly(data=plotly_mesh_fig, width="100%")Keep the figure in a State value and it can change while the app runs. The official docs put a go.Figure in a State var; here State holds only numbers and an @rx.var computed value builds the figure. Move the slider and the computed value is rebuilt and the chart changes.
import math
import plotly.graph_objects as go
class PlotlyWaveState(rx.State):
cycles: int = 2
@rx.event
def set_cycles(self, value: list[float]):
self.cycles = int(value[0])
@rx.var
def figure(self) -> go.Figure:
xs = [i / 50 for i in range(101)]
fig = go.Figure(go.Scatter(x=xs, y=[math.sin(2 * math.pi * self.cycles * x) for x in xs], mode="lines"))
fig.update_layout(title=f"{self.cycles} cycles", height=300)
return fig
rx.vstack(
rx.slider(value=[PlotlyWaveState.cycles], min=1, max=8, step=1, on_change=PlotlyWaveState.set_cycles, width="240px"),
rx.plotly(data=PlotlyWaveState.figure, width="100%"),
width="100%",
)on_click, on_hover and on_selected pass a list of the points the event happened on. Each point carries values such as x, y, curveNumber (which trace) and pointNumber (which point).
import plotly.graph_objects as go
plotly_click_fig = go.Figure(go.Bar(x=["Mon", "Tue", "Wed", "Thu", "Fri"], y=[12, 19, 7, 15, 22]))
plotly_click_fig.update_layout(height=280)
class PlotlyClickState(rx.State):
picked: str = "Click a bar"
@rx.event
def pick(self, points: list[dict]):
if points:
point = points[0]
self.picked = f"{point['x']}: {point['y']}"
rx.vstack(
rx.plotly(data=plotly_click_fig, on_click=PlotlyClickState.pick, width="100%"),
rx.badge(PlotlyClickState.picked, size="2"),
width="100%",
)Set the look with update_layout() when you build the figure. Every layout property, such as the title position (title_x), plot and paper background colors and fonts, is in the Plotly Layout reference.
Rather than giving rx.plotly a fixed width and height, let it follow the size of its outer container. That way it grows and shrinks with the screen.
import plotly.graph_objects as go
plotly_styled_fig = go.Figure(go.Scatter(
x=[1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2007],
y=[68.75, 69.96, 71.3, 72.13, 72.88, 74.21, 75.76, 76.86, 77.95, 78.61, 79.77, 80.65],
mode="lines",
name="Canada",
))
plotly_styled_fig.update_layout(
title="Life expectancy in Canada",
title_x=0.5,
plot_bgcolor="#c3d7f7",
paper_bgcolor="rgba(128, 128, 128, 0.1)",
showlegend=True,
title_font_family="Open Sans",
title_font_size=25,
)
rx.plotly(data=plotly_styled_fig, width="100%")Reflex 0.10 also has lighter bundles that contain only the traces you need. rx.plotly.basic, rx.plotly.cartesian, rx.plotly.finance, rx.plotly.geo, rx.plotly.gl2d, rx.plotly.gl3d, rx.plotly.mapbox and rx.plotly.strict take the same props as rx.plotly while cutting down how much the browser downloads (they are not in the official docs yet).
State holds only the selected product name, and a computed value draws the monthly bars together with a cumulative line. The cumulative line uses the right axis (yaxis2), and hovermode="x unified" gathers a month's values into one box.
import plotly.graph_objects as go
plotly_months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
plotly_sales = {
"Chairs": [32, 41, 38, 52, 61, 58, 70, 66, 59, 73, 88, 95],
"Tables": [12, 15, 21, 19, 24, 30, 28, 33, 35, 31, 40, 46],
"Lamps": [55, 50, 47, 44, 49, 53, 60, 58, 62, 67, 80, 102],
}
class PlotlyOrderState(rx.State):
product: str = "Chairs"
@rx.event
def set_product(self, value: str):
self.product = value
@rx.var
def figure(self) -> go.Figure:
monthly = plotly_sales[self.product]
running = [sum(monthly[: i + 1]) for i in range(len(monthly))]
fig = go.Figure([
go.Bar(x=plotly_months, y=monthly, name="Monthly"),
go.Scatter(x=plotly_months, y=running, name="Running total", yaxis="y2", mode="lines+markers"),
])
fig.update_layout(
title=f"{self.product} sales",
hovermode="x unified",
yaxis2={"overlaying": "y", "side": "right"},
legend={"orientation": "h", "y": -0.2},
height=360,
)
return fig
rx.vstack(
rx.select(["Chairs", "Tables", "Lamps"], value=PlotlyOrderState.product, on_change=PlotlyOrderState.set_product),
rx.plotly(data=PlotlyOrderState.figure, width="100%"),
width="100%",
)Workshop notes
rx.plotly(data=fig) instead of fig.show(). Figures made with go and with px work the same.
Overwrite the layout with layout=dict; change the number format and toolbar language with locale.
Keep simple values in State; when an @rx.var computed value returns a go.Figure, the chart follows.
on_click, on_hover and on_selected pass a list of points (x, y, curveNumber, pointNumber …).
Maps fetch external map data. The theme switches automatically with light and dark mode.
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.