Switch

An on or off setting that takes effect right away. Use a Checkbox when the change waits for a Save button.

Usage

When to use Switch, what its props are for, and the choices it leaves to you.

When to use

A switch is a setting with two states that takes effect as soon as it moves. There is no Save button and no confirm step, so the click is the change, and whatever it controls has to accept that change right away.
  • A preference the reader flips and leaves: dark mode, marketing emails, airplane mode.
  • A feature flag, where turning it on is the whole interaction.
  • One cell of a permissions matrix, where each cell is on or off on its own.
  • A setting with no third state. If “inherit” or “not set” is a real answer, it is not two states.
Use something else when:
  • The change needs a Save, a Cancel or validation. Use a Checkbox in a form: a checkbox records a choice, a switch applies one.
  • There are more than two states. Use Segmented for a view or a mode, and Radio.Group for a form field.
  • The control does something rather than holds a value. “Send now” is a Button.
  • The reader is picking several rows out of a list. Use a checkbox group, where selecting a row applies nothing on its own.
A setting that is slow or dangerous to turn on still suits a switch. Add loading or a confirm step, both below.

Labeling it

Switch renders one <button role="switch"> and nothing else. It has no text of its own, so the words next to it are all that say what the setting is.
  • Wrap it in a <label> to make its text a click target. A click anywhere in the label toggles the switch, and hovering the label shows the switch's hover color. Point aria-labelledby at the title and aria-describedby at the description, or the switch's name becomes the whole label text.
  • A switch with no visible text beside it, like one in a table cell, needs an aria-label.
  • Name the setting, not the action: “Dark mode”, not “Turn dark mode on”. The switch already announces on or off, so the longer name reads as “Turn dark mode on, on”.
  • checkedChildren is not a label. It shows which state the switch is in, not what it controls.
  • Put the consequence in a one-sentence description under the label: what changes, and for whom.
  • Keep the visible words and the accessible name the same, which aria-labelledby does for you, so a voice-control user can say the words they see.

Size

Two sizes. small and the default medium. middle and default still work as older names for medium, and there is no large.
  • Use small where the switch is one of many in a dense surface: a table cell, a toolbar, a row of compact settings, like the permissions matrix example.
  • Compact mode makes both sizes smaller, with no Switch token to change.
  • Keep one size down a column of settings. Readers scan the trailing edge, and two track heights break it.

Shape

shape sets the corners, and the knob follows them. square uses the theme's small radius, squircle a superellipse a little rounder than that, and default the pill. Where the browser has no corner-shape support, squircle looks like square.
  • Match the controls around it: a square switch beside square buttons and inputs, the pill beside pills.
  • Pick one shape for the whole product, not one per screen.
  • To round the knob yourself through classNames.indicator, use a before: class, because the knob is drawn on the handle's ::before.

Loading and disabled

loading disables the switch while it spins, so a second click cannot race the first request.
  • Pick one way to handle a round trip and keep it across the page. Optimistic: flip at once, and roll back with a message if the request fails. Pending: keep the old state, set loading, and flip when the response arrives. The feature flags example does the second for one row.
  • disabled is for a setting this reader cannot change: a plan limit, a missing permission, a dependency that is off. Say which, next to it. A switch that will not move with no reason given looks broken.
  • A Form or ConfigProvider with disabled disables every Switch inside it.
  • Loading and disabled look alike apart from the spinner, which is one more reason to put the reason in text.
  • The loading spinner is Ant Design's own. There is no prop to replace it. To hide it and keep a bare knob, pass classNames={{ indicator: "[&_.anticon]:hidden" }}.

Text inside the track

checkedChildren and unCheckedChildren take any node and show it inside the track: ON and OFF, 1 and 0, a check and a cross.
  • Add them where the state has to be readable without the color, or where the switch sits far from its label.
  • Keep the two about the same width. The track grows with its content, so a long word on one side and a short one on the other resizes the switch every time it flips.
  • Two or three characters is the budget. The text is small and light, on the track color in both states.
  • An Icon in the track follows your icon pack.
  • Neither prop names the switch, and the Accessibility tab explains what they do to its announced name.

Placing it in a settings row

The settings row is the same every time: the label and its description at the start, the switch at the end, pushed apart with justify="space-between". The other Switch examples put that row in different containers.
  • Keep every switch in a group on one trailing edge. Readers scan that column for what is on, so an indented row costs more than it looks.
  • A card grid is the exception. With one setting per card there is no column to line up, so the switch goes in the card's top corner and the text sits under it.
  • Make the whole row the <label>, so a wide container does not mean a long trip to a small track. A label only takes inline content, so use <Flex component="span"> for the column inside it.
  • Anything else clickable in the row, like a help icon or a link, needs preventDefault() in its click handler, or reading the help flips the setting.
  • Put it in a Form with valuePropName="checked". Form.Item binds value by default, so without it the field always reads as off and never reports a change.
  • noStyle on that Form.Item keeps the row's own layout, since the row already draws the label and the spacing.

Guarding a destructive flip

Guard a risky switch with a Popconfirm, and control it. onChange fires on every click, before the user confirms, so set checked from state and turn it on only in onConfirm.
  • Setting the value in onChange flips the track before anything is confirmed, which defeats the confirm.
  • Disable the Popconfirm once the setting is on, so turning it back off (the safe direction) does not ask again.
  • Confirm the dangerous direction only. Asking twice for something harmless teaches people to click through the one that matters.
  • If the change is slow rather than dangerous, use loading instead of a dialog.

Color

color paints the on track. success, warning and error use the status colors. antd's presets (blue, green, gold and the rest) and palettes from your theme (the default theme adds amber, emerald, teal, sky, indigo, violet, fuchsia, rose and grays like slate and zinc) use that hue, with a lighter shade on hover and for the focus ring. A gray palette, a CSS color or an unknown name falls back to primary, because a gray on track reads as off.
  • Keep primary for most switches. A column of settings in five colors asks the reader to decode colors that mean nothing.
  • Use a status color when the on state carries that meaning: error where on is the dangerous direction, success where on is the healthy one.
  • Use another hue for identity: a switch that belongs to a product area or category already color-coded on the page.
  • To change the color of every switch, set colorPrimary or components.Switch in your theme instead of passing color everywhere.
  • Red on a switch means on is the dangerous state, not that the control is invalid. Pair it with a confirm step.
  • Only the knob's position and the track color tell on from off. Add checkedChildren where the state has to be read at a glance.