---
title: Switch icon
summary: The Switch Icon component is used to create a transition between two icons. You can use any icon, both line and filled version.
description: Animate a smooth transition between two icons with the switch icon component - useful for likes, toggles, and favorite buttons.
related: [/ui/components/icon]
source: core/scss/ui/_switch-icon.scss
css-vars: switch-icon
classnames:
component:
- class: switch-icon
desc: Button that swaps between two icons
part:
- class: switch-icon-a
desc: Icon shown in the off state
- class: switch-icon-b
desc: Icon shown in the on state
state:
- class: active
desc: Switched on; Tabler toggles it on click
- class: switch-icon-loading
desc: Spinner instead of the icons, clicks ignored
behavior:
- class: switch-icon-fade
desc: Crossfades between the icons
- class: switch-icon-flip
desc: Flips the icon over
- class: switch-icon-scale
desc: Scales one icon out and the other in
- class: switch-icon-slide-{direction}
desc: Slides the icons; up, down, left, right, start or end
---
import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro';
import CodeDocs from '@components/CodeDocs.astro';
## Default markup
The icon transition is triggered by adding an `.active` class to the `switch-icon` component.
## Switch animations
You can also add a fancy animation to add variety to your button. See demo below:
## In an action button
A switch icon can sit inside a button, for example a `btn-action` in a table row or a card header. Put `data-bs-toggle="switch-icon"` and `aria-pressed` on the button and the `switch-icon` inside it: Tabler toggles the icon and keeps the state on the button, which also provides the hover, focus and target size.
## Loading state
Add `switch-icon-loading` to show a spinner in place of the icons, for example while a request is in flight. The button ignores clicks in this state. Set `aria-busy="true"` with it, so assistive technology knows the control is busy.
## Wait for a request
Every toggle fires `toggle.bs.switch-icon` on the button before the state changes. `event.active` is the state the button is about to switch to. Pass a promise to `event.wait()` and the button shows the loading state until it settles: it switches when the promise resolves and stays as it was when it rejects. Call `event.preventDefault()` to cancel the toggle.
```html
```
```js
document.querySelectorAll('[data-bs-toggle="switch-icon"]').forEach((button) => {
button.addEventListener('toggle.bs.switch-icon', (event) => {
event.wait(fetch(`/api/items/${button.dataset.id}/star`, { method: event.active ? 'PUT' : 'DELETE' }));
});
});
```
The event bubbles, so one listener on a list can serve all of its buttons.
## JavaScript
Tabler wraps the button in a `SwitchIcon` component that works like the Bootstrap components: it is created once per element and stored on it. On page load Tabler creates one for every element with `data-bs-toggle="switch-icon"`, and a click toggles the `active` class and `aria-pressed`. This is the code that runs:
Create the component yourself for a button added later, or change the state from code:
```js
const favorite = tabler.SwitchIcon.getOrCreateInstance(document.getElementById('favorite'));
favorite.toggle(); // flip
favorite.toggle(true); // force on
```
Every toggle fires `toggle.bs.switch-icon` before the change (cancelable, see [Wait for a request](#wait-for-a-request)) and `change.bs.switch-icon` after it, both with the new state in `event.active`:
```js
document.getElementById('favorite').addEventListener('change.bs.switch-icon', (event) => {
console.log(event.active ? 'added to favorites' : 'removed from favorites');
});
```
| Method | Description |
| --- | --- |
| `toggle(force)` | Flips the state, or sets it when `force` is `true` or `false`. Fires `toggle.bs.switch-icon`, then updates `aria-pressed` and fires `change.bs.switch-icon`. Does nothing while loading. |
| `isActive` | Getter. `true` when the switch icon has the `active` class. |
| `isLoading` | Getter. `true` while a promise passed to `event.wait()` is pending. |
| `dispose()` | Removes the click handler and the component from the element. |
| `getInstance(element)` | Static. Returns the component for the element, or `null`. |
| `getOrCreateInstance(element)` | Static. Returns the component for the element and creates it when needed. |
## Accessibility
- A switch icon is a toggle button. Give it `type="button"`, an `aria-label` saying what it toggles, and `aria-pressed` for the state. Tabler keeps `aria-pressed` in sync, and fills it in from the `active` class when the markup leaves it out.
- The two icons are decoration. The state lives in `aria-pressed`, not in which icon is visible.
- Colour alone should not carry the state. A filled red heart and an outlined gray one differ in shape too, which is what makes them readable.
- The button box is at least 24×24px (`$switch-icon-min-size`) with the icon centered inside, to meet the WCAG target size. Set `--tblr-switch-icon-min-size` to change it.
- To disable a switch icon use the `disabled` attribute, or `aria-disabled="true"` when it has to stay focusable. Tabler ignores clicks on both, and on the `disabled` class, which alone would still let Enter and Space toggle it.
- While a request is pending, the button carries `aria-busy="true"` and ignores clicks, so a double click cannot send the request twice.
- The spinner slows down under `prefers-reduced-motion`, and the icon transitions are switched off there.
## Variables
Use these SCSS variables to customize switch icons. The default values are: