Sometimes, you just need to switch things up.
Say, from “on” to “off.” “Light mode” to “dark mode.” “Available” to “unavailable.”
The ever-toggleable React switch component is just the ticket to make that possible. And with React’s component-based architecture, it’s common for developers to build a React switch component that they can import across an application.
Let’s explore how to build a React switch from scratch, the basic component structure, and how to customize a switch component with consideration toward ease of use.
A switch is a common UI component that displays and toggles a state between two possible values. It typically represents a boolean, like “True or False” and “Yes or No”. Additionally, under WAI-ARIA guidelines, there’s a third mixed state reflecting a partially checked checkbox. (Today’s example won’t cover that, but we encourage you to center accessibility and get familiar with those guidelines!)
Also, to be super clear, the switch component we’re working with today is not a JavaScript switch statement—which is a totally different native logic statement.
Building a React switch component makes it easy for a user to handle booleans. With a simple import statement, developers could drop-in a component that has all the bells and whistles to handle toggle events.
There are certain design considerations to keep in mind when building a robust React switch component. Ideally, the component needs to match your app or company’s design language without overcomplicating the switch’s logic or functionality.
The most simple React switch component needs two core input props:
- A
value
variable that defines the switch’s state. This variable could be a number (e.g.,0
and1
) or string (e.g.,“on”
and“off”
). However, it’s cleanest to use the native boolean type (e.g.,true
andfalse
). - A
onChange
function that can be invoked to mutate the switch’s value. It’s important that this be a prop given React’s unidirectional data flow—thevalue
shouldn’t ever be modified directly.
Additionally, a React switch requires a very basic HTML structure. Foremost, it needs a main container div whose background represents the switch’s state. It also needs a togglable switch element—a “handle” or “knob”—that moves upon click.
Technically, this can be accomplished with just a single div element in addition to its ::before
or ::after
properties being repurposed for the handle, but given that React is an abstraction layer that simplifies re-using code, such over-the-top conciseness isn’t generally necessary.
In this example, we’ll use four color states—the color of the switch’s background and the switch’s handle as well as the same when the switch is enabled.
Some switches might bake these colors into the component if the switch is to appear the same throughout the application. But accepting additional props to alter these colors can be a nice optional feature, especially if the switch is used in alternative contexts (e.g., a negative context when the switch is disabling a feature).
It’s important that the toggle switch communicates to the user when it is on versus off. In some cases, we might want to make these colors props so that the component can switch colors in various settings. (For instance, a switch disabling something might be red when on.)
Thankfully, React makes such customization easy. We just have to include two additional props—an `checkedColor` prop and an `uncheckedColor` prop where the valid colors are ingested into the component. Another set of two props—checkedHandleColor and uncheckedHandleColor—are fantastic for altering the handle or knob’s color theme.
There are various user interactions to consider when building a React switch component. These include handling toggle events. Three common props are onToggle, onTrue, and onFalse to fire during the respective events.
These props enable the React switch to trigger logic whenever a user interacts with it.
Finally, it’s important that the React switch contains a native input checkbox element that’s hidden with the correct data binding to the component’s props. This helps keep the underlying form accessible by using native form fields that are cleanly defined to the DOM. It’s quite easy to do this with a checkbox’s native props.
We’ll progressively build out the component, slowly adding more robust functionality to address the previous concerns.
Given a React switch component’s functionality is rather simple, we’ll start out with a simple functional component. To begin, we’ll populate it with a container div and an empty div that’ll become our handle.
1import React from "react";
2const Switch = ({
3 value,
4 onClick,
5}) => {
6 return (
7 <div>
8 <div></div>
9 </div>
10 );
11};
12export default Switch;
13
We’ll add an onClick
function to the container div that’ll change the switch’s state on user interaction.
1import React from "react";
2const Switch = ({
3 value,
4 onClick,
5}) => {
6 return (
7 <div
8 onClick={() => {
9 onClick(!value);
10 }}
11 >
12 <div></div>
13 </div>
14 );
15};
16export default Switch;
17
We’ll now add some conditional CSS styling to reflect state. This could be done using defined CSS classes, but to stick with just JavaScript, we’ll use the native style prop. Here’s where you can really get creative with your switch’s look and feel. For my design, I’ll be utilizing the following techniques:
- An absolutely positioned knob that’s always 3px from the border on either state. Here, using an absolutely positioned div is clean because the switch is a fixed size.
- Conditionally set
backgroundColor
My design might have code that looks like this:
1import React from "react";
2const SWITCH_WIDTH_PX = 72;
3const HANDLE_DIAMETER_PX = 30;
4const SWITCH_OFFSET_PX = 3;
5const Switch = ({
6 value,
7 onClick,
8}) => {
9 return (
10 <div
11 style={{
12 width: SWITCH_WIDTH_PX,
13 height: HANDLE_DIAMETER_PX + 2 * SWITCH_OFFSET_PX,
14 borderRadius: HANDLE_DIAMETER_PX,
15 border: "1px #ddd solid",
16 position: "relative",
17 cursor: "pointer",
18 background: value ? "blue" : "aliceblue",
19 }}
20 onClick={() => {
21 onClick(!value);
22 }}
23 >
24 <div
25 style={{
26 background: value ? “white” : “blue”,
27 borderRadius: "100%",
28 height: HANDLE_DIAMETER_PX,
29 width: HANDLE_DIAMETER_PX,
30 position: "absolute",
31 top: SWITCH_OFFSET_PX,
32 left: value
33 ? SWITCH_WIDTH_PX - HANDLE_DIAMETER_PX - SWITCH_OFFSET_PX
34 : SWITCH_OFFSET_PX,
35 }}
36 ></div>
37 </div>
38 );
39};
40export default Switch;
41
There are near-infinite ways to style your switch, so do whatever best fits your application! (And if you use any React CSS libraries like styled-components, be sure to leverage those for consistency across your application.)
We can use the native CSS transition
property to add animation to our switch. We simply add a transition property to each element in our component—both the container and the handle:
1import React from "react";
2const SWITCH_WIDTH_PX = 72;
3const HANDLE_DIAMETER_PX = 30;
4const SWITCH_OFFSET_PX = 3;
5const Switch = ({
6 value,
7 onClick,
8}) => {
9 return (
10 <div
11 style={{
12 width: SWITCH_WIDTH_PX,
13 height: HANDLE_DIAMETER_PX + 2 * SWITCH_OFFSET_PX,
14 borderRadius: HANDLE_DIAMETER_PX,
15 border: "1px #ddd solid",
16 position: "relative",
17 transition: "1s",
18 cursor: "pointer",
19 background: value ? "blue" : "aliceblue",
20 }}
21 onClick={() => {
22 onClick(!value);
23 }}
24 >
25 <div
26 style={{
27 background: value ? “white” : “blue”,
28 borderRadius: "100%",
29 height: HANDLE_DIAMETER_PX,
30 width: HANDLE_DIAMETER_PX,
31 position: "absolute",
32 top: SWITCH_OFFSET_PX,
33 left: value
34 ? SWITCH_WIDTH_PX - HANDLE_DIAMETER_PX - SWITCH_OFFSET_PX
35 : SWITCH_OFFSET_PX,
36 transition: "1s",
37 }}
38 ></div>
39 </div>
40 );
41};
42export default Switch;
43
This gives the switch a natural one-second transition. Additionally, to meet a more thorough accessibility standard, you can disable transitions if a browser or OS-based “reduce motion” setting is ticked. (Separately, if you’re interested in learning more about animation in React, I cover that in this blog post on React animation libraries.)
Next, let’s add a hidden input box that’ll act as a DOM-recognized form element that also reflects a switch’s state. This helps with the switch’s accessibility.
1import React from "react";
2const SWITCH_WIDTH_PX = 72;
3const HANDLE_DIAMETER_PX = 30;
4const SWITCH_OFFSET_PX = 3;
5const Switch = ({
6 value,
7 onClick,
8}) => {
9 return (
10 <div
11 style={{
12 width: SWITCH_WIDTH_PX,
13 height: HANDLE_DIAMETER_PX + 2 * SWITCH_OFFSET_PX,
14 borderRadius: HANDLE_DIAMETER_PX,
15 border: "1px #ddd solid",
16 position: "relative",
17 transition: "1s",
18 cursor: "pointer",
19 background: value ? "blue" : "aliceblue",
20 }}
21 onClick={() => {
22 onClick(!value);
23 }}
24 >
25 <div
26 style={{
27 background: value ? “white” : “blue”,
28 borderRadius: "100%",
29 height: HANDLE_DIAMETER_PX,
30 width: HANDLE_DIAMETER_PX,
31 position: "absolute",
32 top: SWITCH_OFFSET_PX,
33 left: value
34 ? SWITCH_WIDTH_PX - HANDLE_DIAMETER_PX - SWITCH_OFFSET_PX
35 : SWITCH_OFFSET_PX,
36 transition: "1s",
37 }}
38 ></div>
39 <input
40 type="checkbox"
41 value={value}
42 onChange={(e) => {
43 onClick(e.target.value);
44 }}
45 style={{ display: "none" }}
46 />
47 </div>
48 );
49};
50export default Switch;
51
Using the native onChange function, we can easily toggle the switch’s value should the input box be altered with something like a screen reader.
If you want to alter the switch’s colors, we could also add some conditional declare statements to define these. We’ll scope these out into intra-class variables to avoid cluttering our style inputs that can also be set as props. Altogether, the component is defined as:
1import React from "react";
2const SWITCH_WIDTH_PX = 72;
3const HANDLE_DIAMETER_PX = 30;
4const SWITCH_OFFSET_PX = 3;
5const Switch = ({
6 containerCheckedColor = "blue",
7 containerUncheckedColor = "aliceblue",
8 handleCheckedColor = "white",
9 handleUncheckedColor = "blue",
10 value,
11 onClick,
12}) => {
13 return (
14 <div
15 style={{
16 width: SWITCH_WIDTH_PX,
17 height: HANDLE_DIAMETER_PX + 2 * SWITCH_OFFSET_PX,
18 borderRadius: HANDLE_DIAMETER_PX,
19 border: "1px #ddd solid",
20 position: "relative",
21 transition: "1s",
22 cursor: "pointer",
23 background: value ? containerCheckedColor : containerUncheckedColor,
24 }}
25 onClick={() => {
26 onClick(!value);
27 }}
28 >
29 <div
30 style={{
31 background: value ? handleCheckedColor : handleUncheckedColor,
32 borderRadius: "100%",
33 height: HANDLE_DIAMETER_PX,
34 width: HANDLE_DIAMETER_PX,
35 position: "absolute",
36 top: SWITCH_OFFSET_PX,
37 left: value
38 ? SWITCH_WIDTH_PX - HANDLE_DIAMETER_PX - SWITCH_OFFSET_PX
39 : SWITCH_OFFSET_PX,
40 transition: "1s",
41 }}
42 ></div>
43 <input
44 type="checkbox"
45 value={value}
46 onChange={(e) => {
47 onClick(e.target.value);
48 }}
49 style={{ display: "none" }}
50 />
51 </div>
52 );
53};
54export default Switch;
55
And just like that, we have a fully functional and customizable React switch component!
In this tutorial, we demonstrated how we could create a simple but personalizable React switch component to enable users to toggle boolean functions. The component built here ships with default colors, but those can be customized however you like. Additionally, the React switch is great for accessibility with its embedded native DOM element within the JSX.
Want to skip the lift? The best React component library is in Retool. We’ve got a React switch component ready to go—give it a try.
Reader