Skip to content

RangeSlider

Single or dual range slider with optional live value display.


Quick Start

Live Preview
75
from faststrap import RangeSlider

RangeSlider("score", min_value=0, max_value=100, value=75, label="Score")

Dual Range

RangeSlider(
    "budget",
    dual=True,
    min_selected=200,
    max_selected=1200,
    min_name="min_budget",
    max_name="max_budget",
)

Dual mode renders two range inputs. Validate that min is not greater than max server-side.


Value Display

RangeSlider(
    "score",
    value=25,
    show_value=True,
    value_suffix="%",
)

Step Control

RangeSlider("score", min_value=0, max_value=100, step=5)

API Reference

faststrap.components.forms.range_slider.RangeSlider(name, *, label=None, help_text=None, min_value=0, max_value=100, step=1, value=None, dual=False, min_name=None, max_name=None, min_selected=None, max_selected=None, show_value=True, value_suffix='', **kwargs)

Bootstrap range slider.

Source code in src/faststrap/components/forms/range_slider.py
@register(category="forms")
def RangeSlider(
    name: str,
    *,
    label: str | None = None,
    help_text: str | None = None,
    min_value: int | float = 0,
    max_value: int | float = 100,
    step: int | float = 1,
    value: int | float | None = None,
    dual: bool = False,
    min_name: str | None = None,
    max_name: str | None = None,
    min_selected: int | float | None = None,
    max_selected: int | float | None = None,
    show_value: bool = True,
    value_suffix: str = "",
    **kwargs: Any,
) -> Div:
    """Bootstrap range slider."""
    cfg = resolve_defaults(
        "RangeSlider",
        min_value=min_value,
        max_value=max_value,
        step=step,
        dual=dual,
        show_value=show_value,
        value_suffix=value_suffix,
    )
    c_min_value = cfg.get("min_value", min_value)
    c_max_value = cfg.get("max_value", max_value)
    c_step = cfg.get("step", step)
    c_dual = cfg.get("dual", dual)
    c_show_value = cfg.get("show_value", show_value)
    c_value_suffix = cfg.get("value_suffix", value_suffix)

    slider_id = kwargs.pop("id", name)

    def _build_input(input_name: str, input_value: int | float | None, extra_id: str) -> FTInput:
        attrs: dict[str, Any] = {
            "type": "range",
            "name": input_name,
            "id": extra_id,
            "min": str(c_min_value),
            "max": str(c_max_value),
            "step": str(c_step),
            "cls": "form-range",
        }
        if input_value is not None:
            attrs["value"] = str(input_value)
        attrs.update(convert_attrs(kwargs))
        return FTInput(**attrs)

    inputs: list[Any] = []
    value_display = None

    if c_dual:
        min_field = min_name or f"{name}_min"
        max_field = max_name or f"{name}_max"
        inputs.append(_build_input(min_field, min_selected, f"{slider_id}-min"))
        inputs.append(_build_input(max_field, max_selected, f"{slider_id}-max"))
        if c_show_value:
            display = (
                f"{min_selected if min_selected is not None else c_min_value}"
                f"{c_value_suffix} — "
                f"{max_selected if max_selected is not None else c_max_value}{c_value_suffix}"
            )
            value_display = Span(display, cls="small text-muted")
    else:
        inputs.append(_build_input(name, value, slider_id))
        if c_show_value:
            display_value = value if value is not None else c_min_value
            value_display = Span(f"{display_value}{c_value_suffix}", cls="small text-muted")

    nodes: list[Any] = []
    if label:
        nodes.append(Label(label, **{"for": slider_id}, cls="form-label"))

    nodes.extend(inputs)

    if value_display:
        nodes.append(value_display)

    if help_text:
        help_id = f"{slider_id}-help"
        nodes.append(Small(help_text, cls="form-text text-muted", id=help_id))

    return Div(*nodes, cls="mb-3 faststrap-range-slider")