Files
Htmx/docs/Components/Slider.md
T
2026-05-05 23:55:26 +05:00

6.1 KiB
Raw Blame History

Slider

A draggable range control. Use it when you want the user to pick a numeric value within a range — volume, brightness, price range, font size.


Quick example

new Slider(
    id:    "volume",
    name:  "volume",
    label: "Volume")

Defaults to a 0100 range with a starting value of 50.


All the options

public Slider(
    string id,
    string name         = "",
    int    min          = 0,
    int    max          = 100,
    int    step         = 1,
    int    value        = 50,
    string label        = "",
    string description  = "",
    string extraClasses = "",
    string hxAttrs      = "")
Parameter What it does
id The element id. Also used by <label for="...">.
name Form field name.
min Lowest selectable value.
max Highest selectable value.
step How much the value changes per tick.
value Starting position of the thumb.
label Visible text label above the slider.
description Small hint text below the slider.
extraClasses Additional Tailwind classes on the <input>.
hxAttrs Extra HTML attributes appended verbatim.

Real-world examples

Brightness setting (stepped)

new Slider(
    id:          "brightness",
    name:        "brightness",
    min:         10,
    max:         100,
    step:        10,
    value:       70,
    label:       "Brightness",
    description: "10 to 100")

Font size with live HTMX update

new Slider(
    id:      "fontSize",
    name:    "fontSize",
    min:     12,
    max:     32,
    value:   16,
    label:   "Font size",
    hxAttrs: """hx-post="/settings/font-size" hx-trigger="change"""")

Reading the value in a form handler

public record Command([property: FromForm] int Volume);
// command.Volume == 0..100

How it works

Slider renders a native <input type="range">. The thumb and active track colour follow your primary theme colour via accent-primary. No JavaScript is needed — the browser handles the drag interaction natively.

public record Command([property: FromForm] int Volume);

// command.Volume is the slider value at submit time

Tips and tricks

  • Display the current numeric value next to the slider by adding a small <output> element and a JS input event listener: slider.addEventListener('input', e => output.value = e.target.value). This can be added via hxAttrs: "oninput=\"document.getElementById('vol-val').textContent=this.value\"".
  • value is the initial server-rendered position. After the user moves the slider, only the form submission captures the new value.
  • Use step to snap to meaningful increments (e.g. step: 5 for a 0100 percentage slider).
  • accent-primary is supported in all modern browsers and requires no custom CSS.
  • accent-primary is supported in all modern browsers and requires no custom CSS.

Complete page example

Templates/AudioSettingsPage.htmx

<div class="max-w-md mx-auto py-10">
  <h1 class="text-2xl font-bold mb-6">Audio settings</h1>
  <form method="post" action="/settings/audio">
    $$AntiforgeryToken$$
    <div class="space-y-6 mb-8">
      $$VolumeSlider$$
      $$BassSlider$$
      $$TrebleSlider$$
    </div>
    $$SaveBtn$$
  </form>
  $$SuccessAlert$$
</div>

Templates/AudioSettingsPage.htmx.cs

namespace Htmx.ApiDemo.Templates;

public sealed class AudioSettingsPage : AudioSettingsPageBase
{
    private readonly IHtmxComponent _volume;
    private readonly IHtmxComponent _bass;
    private readonly IHtmxComponent _treble;
    private readonly IHtmxComponent _save;
    private readonly IHtmxComponent _success;
    private readonly byte[] _afToken;

    public AudioSettingsPage(
        IAntiforgery af,
        HttpContext ctx,
        AudioPrefs? prefs = null,
        bool saved = false)
    {
        var tokens = af.GetAndStoreTokens(ctx);
        _afToken = $"""<input type="hidden" name="{tokens.FormFieldName}" value="{tokens.RequestToken}">""".ToUtf8Bytes();

        _volume  = new Components.Slider(id: "volume",  name: "volume",  label: "Volume",  value: prefs?.Volume  ?? 70, description: "0  100");
        _bass    = new Components.Slider(id: "bass",    name: "bass",    label: "Bass",    value: prefs?.Bass    ?? 50, min: -10, max: 10, step: 1);
        _treble  = new Components.Slider(id: "treble",  name: "treble",  label: "Treble",  value: prefs?.Treble  ?? 50, min: -10, max: 10, step: 1);
        _save    = new Components.Button("Save", type: "submit");
        _success = saved ? new Components.Alert(title: "Audio settings saved.") : HtmxEmpty.Instance;
    }

    protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
    protected override void RenderVolumeSlider(HtmxRenderContext ctx)     => _volume.Render(ctx.Next());
    protected override void RenderBassSlider(HtmxRenderContext ctx)       => _bass.Render(ctx.Next());
    protected override void RenderTrebleSlider(HtmxRenderContext ctx)     => _treble.Render(ctx.Next());
    protected override void RenderSaveBtn(HtmxRenderContext ctx)          => _save.Render(ctx.Next());
    protected override void RenderSuccessAlert(HtmxRenderContext ctx)     => _success.Render(ctx.Next());
}

POST handler

[Handler]
[MapPost("/settings/audio")]
public static partial class PostAudioSettingsHandler
{
    public record Command(
        [property: FromForm] int Volume,
        [property: FromForm] int Bass,
        [property: FromForm] int Treble);

    private static Task<IResult> HandleAsync(
        [AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
    {
        var prefs = new AudioPrefs(cmd.Volume, cmd.Bass, cmd.Treble);
        // Persist prefs…
        return ctx.WriteHtmxPage(
            new AudioSettingsPage(af, ctx, prefs, saved: true), title: "Audio settings");
    }
}

public record AudioPrefs(int Volume, int Bass, int Treble);

AppJsonSerializerContext.cs

[JsonSerializable(typeof(PostAudioSettingsHandler.Command), TypeInfoPropertyName = "AudioSettingsCommand")]