RangeSlider#

A two-handle track for selecting a low/high value range.

RangeSlider demo — light theme RangeSlider demo — dark theme

Usage#

A range slider selects a low and a high value within a min_value/max_value range, both clamped to the bounds. As with the single-handle Slider, on_change fires continuously as a handle moves and on_commit fires once on release — reach for on_commit when the per-update work is expensive.

Basic#

bs.RangeSlider(20, 80)
bs.RangeSlider(0, 50, min_value=0, max_value=100)

Show value badges#

Set show_value=True to display floating badges on both handles.

bs.RangeSlider(20, 80, show_value=True)
RangeSlider value badges — light theme RangeSlider value badges — dark theme

Tick marks#

Use tick_step= to add major tick marks. minor_ticks= adds subdivisions between them.

bs.RangeSlider(20, 80, tick_step=20)
bs.RangeSlider(20, 80, tick_step=20, minor_ticks=4)
RangeSlider tick marks — light theme RangeSlider tick marks — dark theme

Note

tick_step only draws the marks — the handles still move continuously. To make the handles snap to those increments, use step= (see Snapping to steps).

Accent colors#

bs.RangeSlider(20, 80, accent="primary")
bs.RangeSlider(20, 80, accent="secondary")
bs.RangeSlider(20, 80, accent="info")
bs.RangeSlider(20, 80, accent="success")
bs.RangeSlider(20, 80, accent="warning")
bs.RangeSlider(20, 80, accent="danger")
RangeSlider accent colors — light theme RangeSlider accent colors — dark theme

Reactive binding#

Bind Signal[float] instances to each handle independently.

lo = bs.Signal(25.0)
hi = bs.Signal(75.0)
bs.RangeSlider(low_signal=lo, high_signal=hi)
lo.subscribe(lambda v: print(f"Low: {v}"))
hi.subscribe(lambda v: print(f"High: {v}"))

Reading the range#

Access both handles at once via .value, or individually via .low_value and .high_value.

rs = bs.RangeSlider(20, 80)
lo, hi = rs.value      # → (20.0, 80.0)
rs.low_value  = 30     # move low handle (clamped to [min, high])
rs.high_value = 70     # move high handle (clamped to [low, max])

Snapping to steps#

Set step= to constrain both handles to discrete increments. Click and drag values snap to the nearest multiple of step (measured from min_value), and the arrow keys move by step. It is independent of tick_step — set both for a stepped range with matching marks, or step alone to snap without drawing ticks.

# Both handles snap to 0, 5, 10, … 100
bs.RangeSlider(20, 80, min_value=0, max_value=100, step=5, tick_step=5)

When the range is not an even multiple of step, the maximum stays reachable.

Events#

on_change fires continuously as either handle moves; on_commit fires once when a handle is released or moved by keyboard. Reach for on_commit when the work behind the slider is expensive — a query, a recompute, a redraw.

rs = bs.RangeSlider(20, 80, min_value=0, max_value=100)

# Fires continuously during a drag — keep this cheap
rs.on_change(lambda e: preview(e.low_value, e.high_value))

# Fires once, on release or keyboard commit
rs.on_commit(lambda e: filter_results(e.low_value, e.high_value))

# As a Stream
rs.on_change().debounce(150).listen(lambda e: save(e.low_value, e.high_value))

The handler receives a RangeSliderEvent (on_change) or RangeSliderCommitEvent (on_commit); event.low_value and event.high_value hold the current handle values.

Keyboard#

The slider is a single tab stop — Tab moves focus in, then on out to the next control; focus is never trapped. The active handle (shown with a focus ring) responds to the along-track arrow keys (±1, or ±``step`` when step is set), Shift + arrow (a larger jump), and Home / End (minimum / maximum). The cross-axis arrows switch which handle is active — Up / Down for a horizontal slider, Left / Right for a vertical one. Every keyboard change also emits on_commit.

Disabled#

bs.RangeSlider(20, 80)
bs.RangeSlider(20, 80, disabled=True)
RangeSlider disabled — light theme RangeSlider disabled — dark theme

Widget sizing#

All widgets accept self-placement kwargs via **kwargs. The parent container determines which options apply — Column / Row parents use the layout kwargs below, grid-based parents use grid kwargs.

Column (vertical layout)

Used inside a Column, App, or any other container with a column layout. Children are arranged top-to-bottom, so horizontal aligns each child across the width and grow shares the vertical space. (vertical does not apply — the order of the children sets their top-to-bottom position.)

horizontal

Cross-axis placement of the widget: 'left', 'center', 'right', or 'stretch' to fill the available width.

grow

Claim and fill a share of the leftover vertical space (the layout direction). True or False.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Row (horizontal layout)

Used inside a Row or any other container with a row layout. Children are arranged left-to-right, so vertical aligns each child across the height and grow shares the horizontal space. (horizontal does not apply — the order of the children sets their left-to-right position.)

vertical

Cross-axis placement of the widget: 'top', 'center', 'bottom', or 'stretch' to fill the available height.

grow

Claim and fill a share of the leftover horizontal space (the layout direction). True or False.

margin

External spacing in pixels. Accepts an integer (equal on all sides), a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing (left and right). Accepts an integer or a 2-tuple (left, right) for asymmetric spacing. Overrides the horizontal component of margin=.

margin_y

Vertical external spacing (top and bottom). Accepts an integer or a 2-tuple (top, bottom) for asymmetric spacing. Overrides the vertical component of margin=.

Grid

Used inside a Grid container.

row / column

Zero-based row and column indices.

rowspan / columnspan

Number of rows or columns to span.

horizontal

Horizontal placement within the grid cell: 'left', 'center', 'right', or 'stretch' to fill the cell width.

vertical

Vertical placement within the grid cell: 'top', 'center', 'bottom', or 'stretch' to fill the cell height.

margin

External spacing in pixels. Accepts an integer, a 2-tuple (horizontal, vertical), or a 4-tuple (left, top, right, bottom).

margin_x

Horizontal external spacing. Accepts an integer or (left, right).

margin_y

Vertical external spacing. Accepts an integer or (top, bottom).

See also#

API#

The complete reference for RangeSlider lives on the Widgets API page. At a glance:

RangeSlider

A two-handle slider for selecting a low/high value range.

Full Example#

 1
 2with bs.App(title="RangeSlider Demo", padding=20, gap=16, minsize=(400, 200)) as app:
 3
 4    # Basic
 5    bs.Label("Basic", font="heading-sm")
 6    bs.RangeSlider(20, 80, horizontal="stretch")
 7
 8    # Value badges
 9    bs.Label("Value Badges", font="heading-sm")
10    bs.RangeSlider(20, 80, show_value=True, horizontal="stretch")
11
12    # Tick marks
13    bs.Label("Tick Marks", font="heading-sm")
14    bs.RangeSlider(20, 80, tick_step=20, horizontal="stretch")
15    bs.RangeSlider(20, 80, tick_step=20, minor_ticks=4, show_value=True, horizontal="stretch")
16
17    # Accent colors
18    bs.Label("Accent Colors", font="heading-sm")
19    with bs.Column(gap=6, horizontal="stretch", horizontal_items="stretch"):
20        for accent in ("primary", "secondary", "info", "success", "warning", "danger"):
21            bs.RangeSlider(20, 80, accent=accent)
22
23    # Step snapping (both handles snap to 0, 5, 10, … 100)
24    bs.Label("Step Snapping", font="heading-sm")
25    bs.RangeSlider(20, 80, step=5, tick_step=5, horizontal="stretch")
26
27    # Disabled
28    bs.Label("Disabled", font="heading-sm")
29    bs.RangeSlider(20, 80, disabled=True, horizontal="stretch")
30
31app.run()