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.sliderOfficial docs
Workshop order
Add a price range filter to the product list. Two handles pick the lowest and highest price, and the chosen range shows right away.
rx.slider is a bar with a handle you drag to pick a number within a range. Use it for numbers where the rough position matters more than the exact value: volume, brightness, a price range. Give it one value for one handle, or a list of two for a range slider.
rx.slider(default_value=40)Change max and step, then drag the handle. It moves in steps of step.
Try the props
max
100
step
1
size
variant
color
radius
high_contrast
disabled
rx.slider(default_value=40, max=100, step=1, size="2", variant="surface", color_scheme="indigo", radius="full", high_contrast=False, disabled=False)| Prop | Values | Default | Use |
|---|---|---|---|
default_value | number / list of numbers | [50] | Initial value (uncontrolled). Two values make a range slider |
value | list of numbers / Var | none | Controlled value. A [value] list even with a single handle |
min, max, step | number | 0, 100, 1 | Range and step |
orientation | "horizontal" "vertical" | "horizontal" | Direction |
size | "1" – "3" | "2" | Thickness |
variant | "classic" "surface" "soft" | "surface" | Look |
color_scheme, radius, high_contrast | theme value | Color, corners ("none" "small" "full"), contrast | |
width | CSS length | "100%" | Width |
name, disabled | Form field name, disable | ||
on_change | event | Continuously while dragging. Passes a list of values | |
on_value_commit | event | Once when the handle is released. Passes a list of values |
on_value_commit fires once, the moment you release the handle. The value always arrives as a list such as [50], even with one handle, so take it out with value[0]. Since it doesn't call the server while you drag, it is lightweight.
class SliderCommitState(rx.State):
value: int = 50
@rx.event
def set_end(self, value: list[int | float]):
self.value = int(value[0])
rx.vstack(
rx.heading(SliderCommitState.value, size="5"),
rx.slider(default_value=50, on_value_commit=SliderCommitState.set_end),
width="100%",
)Give default_value a list of two values and you get two handles. Both values must lie between min and max. The handler receives [start, end].
The official docs' example writes min_=0, but in 0.10 min_ is passed through as a CSS style and does not become the slider's minimum. Write the minimum as min=0.
class SliderRangeState(rx.State):
value_start: int = 25
value_end: int = 75
@rx.event
def set_end(self, value: list[int | float]):
self.value_start = int(value[0])
self.value_end = int(value[1])
rx.vstack(
rx.hstack(
rx.heading(SliderRangeState.value_start, size="5"),
rx.heading(SliderRangeState.value_end, size="5"),
),
rx.slider(
default_value=[25, 75],
min=0,
max=100,
size="1",
on_value_commit=SliderRangeState.set_end,
),
width="100%",
)on_change fires continuously while the handle moves. Left alone, that sends far too many events, so .throttle(100) makes it send at most once every 100 ms.
class SliderLiveState(rx.State):
value: int = 50
@rx.event
def set_end(self, value: list[int | float]):
self.value = int(value[0])
rx.vstack(
rx.heading(SliderLiveState.value, size="5"),
rx.slider(
default_value=50,
min=0,
max=100,
on_change=SliderLiveState.set_end.throttle(100),
),
width="100%",
)Give value the State value wrapped in a list ([State.volume]) and the handle always shows that value. In that case the handle only moves if on_change updates the State. When a button changes the State, the handle moves too.
30
class SliderControlState(rx.State):
volume: int = 30
@rx.event
def set_volume(self, value: list[int | float]):
self.volume = int(value[0])
@rx.event
def mute(self):
self.volume = 0
rx.hstack(
rx.icon("volume-2", size=18),
rx.slider(
value=[SliderControlState.volume],
on_change=SliderControlState.set_volume.throttle(100),
max=100,
step=5,
flex="1",
),
rx.text(SliderControlState.volume, size="2", min_width="2em"),
rx.button("Mute", on_click=SliderControlState.mute, size="1", variant="soft"),
width="100%",
align="center",
)With orientation="vertical" the slider stands upright. A vertical slider needs a parent with a height to be visible, and giving it width="auto" instead of the default "100%" makes it take only as much room as the bar is thick. The third slider has step=2, so it only stops at 0, 2, 4 … 10.
rx.hstack(
rx.slider(default_value=30, orientation="vertical", width="auto"),
rx.slider(default_value=[20, 60], orientation="vertical", width="auto", color_scheme="green"),
rx.slider(default_value=4, min=0, max=10, step=2, orientation="vertical", width="auto", color_scheme="orange"),
height="140px",
spacing="6",
)With a name, it goes into the form data as name: value. The value arrives as a string ("40"), so convert it with int() to use it as a number. A slider with two handles submits its value twice under the name name[], but in 0.10 form data is a dict, so only the last value with that name survives. For a range, reading it through the State is the reliable way.
Results:
{}class SliderFormState(rx.State):
form_data: dict = {}
@rx.event
def handle_submit(self, form_data: dict):
self.form_data = form_data
rx.card(
rx.vstack(
rx.form.root(
rx.hstack(
rx.slider(default_value=40, name="slider"),
rx.button("Submit", type="submit"),
width="100%",
align="center",
),
on_submit=SliderFormState.handle_submit,
reset_on_submit=True,
width="100%",
),
rx.divider(),
rx.hstack(rx.text("Results:", weight="bold"), rx.badge(SliderFormState.form_data.to_string())),
width="100%",
),
width="100%",
max_width="420px",
)The two handles are controlled with value=[low, high], and on_change is throttled to 100 ms so the range display keeps up while you drag. With step=10, prices move in steps of 10.
Price
$20 - $80$0
$200
class SliderPriceState(rx.State):
low: int = 20
high: int = 80
@rx.event
def set_price(self, value: list[int | float]):
self.low, self.high = int(value[0]), int(value[1])
rx.card(
rx.vstack(
rx.hstack(
rx.text("Price", weight="bold"),
rx.spacer(),
rx.badge("$", SliderPriceState.low, " - $", SliderPriceState.high, variant="soft"),
width="100%",
),
rx.slider(
value=[SliderPriceState.low, SliderPriceState.high],
on_change=SliderPriceState.set_price.throttle(100),
min=0,
max=200,
step=10,
),
rx.hstack(rx.text("$0", size="1"), rx.spacer(), rx.text("$200", size="1"), width="100%", color_scheme="gray"),
width="100%",
),
width="100%",
max_width="360px",
)Workshop notes
rx.slider(default_value=40). A list of two makes a range slider.
Handlers always receive a list of values. With one handle, use value[0].
Once on release: on_value_commit. While dragging: on_change + .throttle(100).
Control it with value=[State.x] + on_change. The minimum is min= (not min_).
In a form, the value arrives as a string, like name: "40".
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.