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

6.2 KiB

Switch

An on/off toggle that looks like a physical light switch. Use it for settings where the effect is immediate or where a simple checked/unchecked checkbox would feel too plain — "Enable notifications", "Dark mode", "Maintenance mode".


Quick example

new Switch(
    id:        "notifications",
    label:     "Enable notifications",
    name:      "enableNotifications",
    isChecked: true)

All the options

public Switch(
    string id,
    string label     = "",
    string name      = "",
    bool   isChecked = false)
Parameter What it does
id The element id for the hidden checkbox.
label Optional text shown to the right of the toggle.
name Form field name — required if you want the value submitted.
isChecked Whether the switch is on by default.

Real-world examples

Preferences form with multiple toggles

new Switch(id: "email-alerts",   label: "Email alerts",   name: "emailAlerts",   isChecked: prefs.EmailAlerts)
new Switch(id: "push-notifs",    label: "Push notifications", name: "pushNotifs", isChecked: prefs.PushNotifs)
new Switch(id: "weekly-summary", label: "Weekly digest",  name: "weeklySummary", isChecked: prefs.WeeklySummary)

Reading on the server:

public record Command(
    [property: FromForm] string? EmailAlerts   = null,  // null = off
    [property: FromForm] string? PushNotifs    = null,
    [property: FromForm] string? WeeklySummary = null
);

bool emailAlerts = command.EmailAlerts != null;

Important: Like a checkbox, an unchecked switch is not included in the form submission. Always use string? (nullable) with a default of null.

Toggle without a label (e.g. in a table row)

new Switch(id: $"active-{user.Id}", name: "isActive", isChecked: user.IsActive)

How it works

Switch is a styled <label> wrapping a hidden <input type="checkbox">. JavaScript in components.js listens for clicks and animates the visible track and thumb. The hidden checkbox holds the actual state and is what gets submitted with a form. Because it is a real checkbox under the hood, the form submission behaviour is identical to a plain Checkbox component.


```html
<!-- Parent form with HTMX:
<form hx-post="/settings" hx-trigger="change from:#maintenance">
  $$MaintenanceSwitch$$
</form>
-->

Tips and tricks

  • The hidden checkbox carries the value "on" when checked (standard checkbox default). If you need "true", add value="true" by subclassing or via a wrapper form.
  • Because the click is handled on the <label> element, the switch works correctly even when the hidden input is not directly clicked.
  • For an HTMX auto-save switch, trigger on change from the hidden checkbox using hx-trigger="change from:#myId" on a parent element.
  • Pairing isChecked with a server-read preference ensures the switch reflects the saved state on every page load.
  • Pairing isChecked with a server-read preference ensures the switch reflects the saved state on every page load.

Complete page example

Templates/NotificationsPage.htmx

<div class="max-w-md mx-auto py-10">
  <h1 class="text-2xl font-bold mb-6">Notifications</h1>
  <form method="post" action="/notifications">
  $$AntiforgeryToken$$
  <div class="space-y-5 mb-8">
    $$EmailSwitch$$
    $$PushSwitch$$
    $$SmsSwitch$$
  </div>
  $$SaveBtn$$
  </form>
  $$SuccessAlert$$
</div>

Templates/NotificationsPage.htmx.cs

namespace Htmx.ApiDemo.Templates;

public sealed class NotificationsPage : NotificationsPageBase
{
  private readonly IHtmxComponent _email;
  private readonly IHtmxComponent _push;
  private readonly IHtmxComponent _sms;
  private readonly IHtmxComponent _save;
  private readonly IHtmxComponent _success;
  private readonly byte[] _afToken;

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

    _email   = new Components.Switch(id: "email-notif",  label: "Email notifications",  name: "emailNotif",  isChecked: prefs?.Email ?? true);
    _push    = new Components.Switch(id: "push-notif",   label: "Push notifications",   name: "pushNotif",   isChecked: prefs?.Push  ?? false);
    _sms     = new Components.Switch(id: "sms-notif",    label: "SMS notifications",    name: "smsNotif",    isChecked: prefs?.Sms   ?? false);
    _save    = new Components.Button("Save", type: "submit");
    _success = saved ? new Components.Alert(title: "Notification preferences saved.") : HtmxEmpty.Instance;
  }

  protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
  protected override void RenderEmailSwitch(HtmxRenderContext ctx)      => _email.Render(ctx.Next());
  protected override void RenderPushSwitch(HtmxRenderContext ctx)       => _push.Render(ctx.Next());
  protected override void RenderSmsSwitch(HtmxRenderContext ctx)        => _sms.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("/notifications")]
public static partial class PostNotificationsHandler
{
  public record Command(
    [property: FromForm] string? EmailNotif = null,
    [property: FromForm] string? PushNotif  = null,
    [property: FromForm] string? SmsNotif   = null);

  private static Task<IResult> HandleAsync(
    [AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
  {
    var prefs = new NotificationPrefs(
      Email: cmd.EmailNotif != null,
      Push:  cmd.PushNotif  != null,
      Sms:   cmd.SmsNotif   != null);
    // Persist prefs…
    return ctx.WriteHtmxPage(
      new NotificationsPage(af, ctx, prefs, saved: true), title: "Notifications");
  }
}

public record NotificationPrefs(bool Email, bool Push, bool Sms);

AppJsonSerializerContext.cs

[JsonSerializable(typeof(PostNotificationsHandler.Command), TypeInfoPropertyName = "NotificationsCommand")]