What is a Bootstrap toggle switch?
Strip away the styling and you’re left with a checkbox input. Put .form-switch on its wrapper and it becomes an on/off switch that needs no JavaScript to render or to flip.
It’s the same styled Bootstrap checkbox used elsewhere in a form, wrapped in .form-check. The switch class only changes how that checkbox draws itself.
The thumb isn’t an element at all. Bootstrap paints it as an SVG circle set as the input’s background image, then slides it with background-position (Bootstrap 5.3.8 compiled CSS, 2025).
A few things get mistaken for it. A .btn-check toggle looks like a button but is still a checkbox, and screen readers announce it as checked or not checked (Bootstrap docs, 2025). Bootstrap Toggle and Bootstrap Switch share the name but are separate plugins built on jQuery, and Bootstrap’s v5 migration guide lists jQuery as dropped. The form itself still submits a plain checkbox, so nothing new is happening at the input level.
The markup and classes below target Bootstrap 5.3.8, the latest 5.3.x release in the docs checked in October 2026.
How to create a Bootstrap toggle switch
Load Bootstrap’s CSS, then wrap a checkbox in a div that carries both .form-check and .form-switch. The label’s for has to match the input id.
- Load the CSS by adding the jsDelivr link tag from the Bootstrap download page, or run
npm install bootstrap@5.3.8. - Skip the JavaScript bundle unless the page uses other Bootstrap plugins. The switch itself is pure CSS.
- Create the wrapper, a
div.form-check.form-switch. - Add the input with
form-check-input, type="checkbox", role="switch" and a unique id. - Add the label with
form-check-label and a matching for.
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="notifySwitch">
<label class="form-check-label" for="notifySwitch">Email notifications</label>
</div>
Markup structure
The input and label are siblings, not parent and child. Bootstrap styles the label through the sibling selector, and the for and id pair is what connects the two for clicks and screen readers, so every docs example uses it (Bootstrap docs, 2025).
.form-check wraps everything as a block with a minimum height.form-switch swaps the square box for a track and thumb.form-check-input is the checkbox itself- the label gets
.form-check-label, which dims when the input is disabled
Inside a larger Bootstrap form, the switch takes the same wrapper as any other check, so it stacks and spaces like its neighbors.
One mistake to watch for. The selector is .form-switch .form-check-input, so putting .form-switch on the input itself does nothing and the control stays a square box.
Checked, disabled and focus states
Add checked to start in the on position and disabled to lock the control.
| State | How to set it | What changes |
|---|
| Checked | checked attribute | Track fills with the primary color, thumb moves right and turns white |
| Disabled | disabled attribute | Input and label dim, pointer events stop |
| Disabled and checked | both attributes | On position kept, dimmed |
| Focus | Tab key or click | Thumb turns light blue, focus ring appears |
Bootstrap turns off the thumb’s slide transition for visitors who set prefers-reduced-motion (compiled CSS, Bootstrap 5.3.8).
Changing the size, color and label position of a Bootstrap switch
Bootstrap 5.3 handles switch size and color through Sass variables, and label position through two modifier classes. There’s no size class in 5.3, so the default dimensions are the numbers you end up overriding.
The defaults, pulled from the compiled sources:
- The track is 2em wide, from
$form-switch-width (Bootstrap 5.3.8 docs, 2025) - The wrapper has 2.5em of left padding (compiled CSS, Bootstrap 5.3.8)
- Disabled opacity sits at 0.5 on both input and label (compiled CSS, Bootstrap 5.3.8)
- Bootstrap 6.0.0-alpha.1 defaults to a height of 1.25rem (Bootstrap 6.0 docs)
- Its default width is 1.75 times the height (Bootstrap 6.0 docs)
Size
On 5.3.8 there’s no size class and no CSS custom property for size. Override $form-switch-width in Sass before the variables import, or set a larger font-size on the .form-switch wrapper so padding, margin and width scale together.
Bootstrap 6.0.0-alpha.1 is friendlier here. It adds .switch-sm and .switch-lg on the wrapper, plus --bs-switch-width and --bs-switch-indicator-width for custom sizes.
Sources disagree on a 5.x size variable. fastbootstrap’s switch page documents --bs-form-switch-width and --bs-form-switch-height, yet the .form-switch rules in the compiled Bootstrap 5.3.8 stylesheet define only --bs-form-switch-bg, so on a stock CDN build those two names do nothing.
Without a Sass build, plain CSS aimed at .form-switch .form-check-input does the same job. The approach to overriding Bootstrap’s default CSS without touching the source files applies here unchanged.
Checked and thumb color
The checked track takes the primary theme color, set by $form-check-input-checked-bg-color. The unchecked thumb comes from $form-switch-color, a translucent black, and the focused thumb from $form-switch-focus-color. Once the switch is on, $form-switch-checked-color takes over, and it’s white by default.
Because the thumb is a background image, color and background-color never reach it. Each state stores its own SVG in --bs-form-switch-bg, and a CSS override has to replace that value state by state.
.form-switch .form-check-input:checked {
background-color: #198754;
border-color: #198754;
}
That recolors the track only. The thumb keeps its white fill.
Label position
For a label on the left, add .form-check-reverse to the same element as .form-switch, so the wrapper reads .form-switch.form-check-reverse. A row layout needs .form-check-inline on each wrapper. If there’s no visible text at all, put aria-label on the input.
Bootstrap’s forms documentation still recommends a visible label where possible, for both accessibility and usability (Bootstrap docs, 2025).
How do you read a Bootstrap switch value in JavaScript?
Listen for the change event on the input and read its checked property. It’s true when the switch is on and false when it’s off.
const sw = document.querySelector("#notifySwitch");
sw.addEventListener("change", () => {
console.log(sw.checked);
});
Per MDN (2025), the change event fires when the user checks or unchecks the box, by click or by keyboard. The whole handler is a few lines of plain JavaScript, and Bootstrap 5 no longer needs jQuery for it.
Attribute versus property matters here. The checked attribute sets only the default state, while the checked property tracks the live one (MDN, 2025). MDN also describes change as a user event, so after setting checked in a script, call your update logic yourself.
The value property is another snag. It holds a string that defaults to “on” when no value attribute is set (MDN, 2026), so read checked to learn the state.
A form behaves differently from a script. MDN states that an unchecked checkbox sends neither its name nor its value, and plain HTML has no way to send an off value (MDN, 2026).
| Switch state | Markup | Submitted data for name=”setting” |
|---|
| On | value=”yes” | setting=yes |
| On | no value attribute | setting=on |
| Off | any | nothing |
MDN’s workaround is a hidden input, added by script, that carries the off value.
For a switch that saves a setting the moment it flips, skip the submit and post the new state with an Ajax request from the change handler.
Accessibility requirements for a Bootstrap switch
A switch passes the basics once the input carries role=”switch” and a visible label names the setting. That label shouldn’t name the state, and the Space key has to flip the control.
The ARIA switch role makes screen readers announce on and off instead of checked and unchecked. Bootstrap’s docs say to consider the role and note that older assistive technology falls back to announcing a regular checkbox (Bootstrap docs, 2025).
Keep the label constant. The W3C’s ARIA Authoring Practices Guide (2026) calls it critical that a switch label does not change with its state, so “Email notifications” stays as is whether the control is on or off. Try reading the label aloud with “on” or “off” appended, and rewrite it if it stops making sense (Nielsen Norman Group, 2018).
Space is the key that matters. The guide requires it and lists Enter as optional, and a native checkbox already answers to Space. As for states, MDN documents no mixed state for the switch role, and a mixed aria-checked value is treated as false.
Group related switches in a fieldset with a legend, and point helper text at the input with aria-describedby. The fieldset comes from the guide’s own examples and aria-describedby from the pattern’s list of properties, and both carry over to any accessible forms work on the same page.
There’s also a native switch attribute now. Safari 17.4 shipped <input type="checkbox" switch>, announced in WebKit’s post of February 28, 2024. It uses the ARIA switch role underneath, and browsers without support treat the input as an ordinary checkbox.
Bootstrap’s docs recommend adding it for haptic feedback on iOS 17.4 and later, with no visual change because Bootstrap draws its own switch (Bootstrap docs, 2025).
MDN still labels the attribute experimental with limited browser support (MDN, 2026), so treat it as an enhancement on top of role="switch". Bootstrap 6’s own switch example uses both.
Bootstrap 4, 5 and 6 switch classes compared
Bootstrap 4 builds the switch on .custom-control.custom-switch. Bootstrap 5 uses .form-check.form-switch, and the Bootstrap 6 alpha goes with a .switch wrapper around the input.
| Item | Bootstrap 4.6 | Bootstrap 5.3 | Bootstrap 6 alpha |
|---|
| Wrapper | .custom-control .custom-switch | .form-check .form-switch | .switch |
| Input | .custom-control-input | .form-check-input | Checkbox with role="switch" and switch |
| Label | .custom-control-label | .form-check-label | Plain label, optionally in .form-field |
| Thumb drawn by | Label pseudo-elements | Input background image | Indicator under an invisible checkbox |
| Status, October 2026 | End of life | Latest 5.3.x | Alpha 1 |
Bootstrap’s 4.6.2 announcement set end of life for January 1, 2023, and the first Bootstrap 6 alpha followed on October 8, 2026 (Bootstrap blog, 2022 and 2026).
Going from 4 to 5 is mostly renaming. The markup keeps its shape, an input and a sibling label inside one wrapper, and only the class names change. Bootstrap 5 folds the custom-control classes into .form-check and the custom switch into .form-switch (Bootstrap 5 migration guide).
The jump from 5 to 6 is rougher. Version 6 positions the thumb absolutely and styles it through indicator tokens such as --bs-switch-indicator-bg. The migration guide warns that themes overriding the old padding-based behavior need updating, so treat any --bs-form-switch-bg override from version 5 as a rewrite.
Color moves from Sass variables to a class, because .theme-success on the .switch wrapper sets the checked background and border (Bootstrap 6.0 docs).
The .btn-check class from toggle buttons also moves. In version 6 it sits on the label, with the input nested inside it.
Version 6 raises the browser floor to Chrome and Edge 130, Firefox 132 and Safari 18, where version 5 supported Chrome and Firefox 60 and Safari 12 (Bootstrap 6 migration guide).
Below that floor pages do not render correctly, because version 6 ships no fallbacks or polyfills, so treat the move as a cross-browser compatibility call before it is a markup task.
Native switch or plugin: which one should you use?
On Bootstrap 5, use the native .form-switch. A plugin earns its place only when the design needs On and Off text inside the control or the page is stuck on an older Bootstrap.
| Option | Bootstrap versions | jQuery | Main risk |
|---|
Native .form-switch | 5.x | No | On/Off text needs custom CSS |
| bootstrap4-toggle | 4 | Yes | Needs jQuery, which Bootstrap 5 dropped |
| Bootstrap Switch | 2 (no longer official), 3 and 4 | Yes | Not documented for Bootstrap 5 |
| bootstrap-switch-button | 4 and later | Unclear | README and examples disagree on jQuery |
| bootstrap5-toggle | 5 | Optional | A third-party dependency for a cosmetic feature |
Version ranges come from each project’s own pages: the Bootstrap Switch README (v3.4.0), the gitbrent READMEs for bootstrap4-toggle and bootstrap-switch-button, and the bootstrap5-toggle README.
What plugins add is mostly cosmetic. bootstrap4-toggle takes custom on and off text, four sizes (large, normal, small, mini), onstyle and offstyle colors, and on, off, toggle, enable, disable and destroy methods through jQuery (bootstrap4-toggle README).
Both gitbrent READMEs tell you to listen for change on the checkbox itself instead of a plugin event, so the handler pattern from the scripting section stays the same.
bootstrap5-toggle is the Bootstrap 5 successor, version 5.5.0 on npm as of October 2026. It ships one build without jQuery and one with it, and takes the same kind of on and off labels and styles (bootstrap5-toggle README).
bootstrap-switch-button’s README table lists it as Bootstrap 4+ with no dependencies, yet its usage examples call jQuery with $("#chkSwitch").switchbutton(). Test it on a real Bootstrap 5 build before trusting either statement.
Tutorials such as tutorialspoint’s still walk through Bootstrap Toggle 2.2.2 for this job, which adds a plugin where one class would do on Bootstrap 5. If the design needs text inside the track, a hand-built CSS toggle switch avoids both the plugin and jQuery.
When a toggle switch is the wrong control
Use a checkbox instead when a change needs a Save or Submit button, because a switch promises an immediate effect.
Nielsen Norman Group (2018) warns against placing toggles in long forms where other fields wait for Submit, since users cannot tell whether the toggle already applied. That doubt is a usability problem a plain checkbox avoids.
In the United Airlines iOS app, tapping a toggle changed its look and nothing else, and NN/g judged a single checkbox the better fit.
- Picking several items from a list calls for checkboxes, since a toggle covers exactly one option (NN/g)
- Picking one item from several calls for radio buttons
- A select-all parent that shows partial selection needs a checkbox, because the switch role has no mixed state (MDN) and
:indeterminate never matches a switch (WebKit) - Changes that only apply after an Apply button should use a checkbox too
NN/g, WebKit and the W3C guide each reach the same test from a different direction. NN/g asks whether the change is immediate. WebKit wants to know if users read the control as a setting that is on or off, and the W3C guide’s question is whether on and off or checked and unchecked reads better to a screen reader user.
Bootstrap ships no save logic for a switch, so the immediate effect is your handler’s job. If the request fails, put the switch back and say so, because a control that flips and does nothing is the failure NN/g flagged in the airline app.
Bootstrap Toggle Switch FAQ
Can a Bootstrap switch show On and Off text inside the track?
Not through a built-in option in Bootstrap 5.3. A plugin such as bootstrap5-toggle accepts custom on and off text (bootstrap4-toggle does the same on Bootstrap 4), or custom CSS can add it.
Show both words, because Nielsen Norman Group (2018) warns that a lone descriptor beside the control reads as its label.
Does a Bootstrap switch work in dark mode?
Yes. Bootstrap 5.3.0 added color modes, and data-bs-theme="dark" on the html element or on a single wrapper switches the control (Bootstrap docs, 2025).
The dark thumb has its own Sass variable, $form-switch-color-dark, so a custom thumb color needs a dark counterpart.
How do I add a Bootstrap switch in React?
Render Form.Check with type="switch", an id and a label, or use the Form.Switch alias that wraps it (React-Bootstrap docs, 2026).
The v2 line (latest stable release 2.10.10) targets Bootstrap 5, and v3 is still in beta (3.0.0-beta.6) and also targets Bootstrap 5. The v1.x line targets Bootstrap 4 and is no longer maintained.
Which Vue library works with Bootstrap 5 switches?
BootstrapVueNext. Its docs describe an entirely new implementation on Vue 3 and Bootstrap 5, so it is not a drop-in replacement.
The original BootstrapVue stays on Vue 2 and Bootstrap 4.
Why does my Bootstrap switch render as a plain checkbox?
Usually a version mismatch. The wrapper classes differ in every release, so custom-switch markup has no effect on a Bootstrap 5.3 stylesheet, and form-switch has none on 4.6.
Check which CSS file the page loads before editing the markup.
Should New Projects Start on Bootstrap 5.3 or 6?
A new Bootstrap toggle switch should start on Bootstrap 5.3.8 markup with .form-switch, because version 6 is still in alpha.
Bootstrap’s blog announced 5.3.8 as the last patch before v5.4.0, and no 5.4.0 release is listed on npm as of October 29, 2026 (Bootstrap blog, 2025). A 5.4.0 release or an end-of-life notice for version 5 would change this advice.
Bootstrap 4 reached end of life roughly 20 months after Bootstrap 5 became the default on May 5, 2021, a precedent for the migration window.
Markup renames are cheap. Overrides are what break on migration, so browser analytics should settle the timing.
- Write markup against
.form-switch on 5.3.8 and keep every size and color override in one stylesheet - Test the browser floor before moving to 6