Learn how to create a toggle switch in react.
Toggle switch is used for boolean representation of any thing. For example, Visible or Not, Dark Or Light mode, Yes or No like this.
A checkbox can be used be do the same but designing some thing interactive will result in better user experience.
And as we will be creating this our self instead of using any third party package, it will help us to extend or change the functionality and design at any given time.
Following is the list of props we are going to take in our component.
name: PropTypes.string,
defaultChecked: PropTypes.bool,
onChange: PropTypes.func,
checked: PropTypes.bool,
className: PropTypes.string,
rounded: PropTypes.bool,
variant: PropTypes.oneOf(["primary", "success", "danger"]),
/* Children to show on active state */
checkedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
/* Children to show on inactive state */
uncheckedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
innerRef: PropTypes.instanceOf(Element)
Our component toggle button can be either square or circle, Default is square. So in order to override it we are accepting the rounded prop.
checkedChildren and uncheckedChildren can be any one of string, number, element which will be shown on their respective state next to the toggle button.
All other props are self explanatory and their is nothing special about them.
The structure of our component will be composed of three different parts.
- Wrapper:- This will hold our toggle switch elements and help us to design it with only using CSS.
- Checkbox:- We will be using this to maintain the state, that is checked(active) or not.
- Overlay:- Actual toggle switch design which will be placed over the checkbox.
To create our component we will need help of some extra packages.
prop-types: It is used to validate the props we want to receive.classnames: This helps us to use CSS classes as javascript objects, but to make this work we just have to name the CSS file asfilename.module.css.lodash: It contains list of helpful util functions which fasten the development.
Following is the folder structure of our component.

I guess you now have some better understanding of our component, so let us start creating it.
First, import all the required packages and setup the boilerplate.
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";
import { isEqual } from "lodash";
class ToggleSwitch extends Component {
//Other code will go here
}
export default React.forwardRef((props, ref) => {
return <ToggleSwitch {...props} innerRef={ref} />;
});
Validate the props initially to make sure we don’t missing anything going further.
static propTypes = {
name: PropTypes.string,
defaultChecked: PropTypes.bool,
onChange: PropTypes.func,
checked: PropTypes.bool,
className: PropTypes.string,
rounded: PropTypes.bool,
variant: PropTypes.oneOf(["primary", "success", "danger"]),
/* Children to show on active state */
checkedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
/* Children to show on inactive state */
uncheckedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
innerRef: PropTypes.instanceOf(Element)
};
static defaultProps = {
defaultChecked: false,
checked: false,
variant: "primary"
};
Set the default props in case required props are missing.
Set the state accordingly based on the props received.
constructor(props) {
super(props);
const { checked, defaultChecked } = this.props;
this.state = {
checked: defaultChecked || checked || false
};
}
Now, combine the CSS classes for each part of the component and render the component.
render() {
const { checked } = this.state;
const {
rounded,
checkedChildren,
uncheckedChildren,
className,
name,
variant,
innerRef
} = this.props;
const _className = cx(styles.wrapper, className);
const _slideClassName = cx(styles.slider, styles[variant], {
[styles.round]: rounded
});
const _checkedChildrenClassName = cx(styles.children, styles.checked, {
[styles.visible]: checked
});
const _uncheckedChildrenClassName = cx(styles.children, styles.unchecked, {
[styles.visible]: !checked
});
return (
<span className={_className}>
<span className={styles.switch}>
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleChange}
name={name}
ref={innerRef}
/>
{/* Overlay */}
<span className={_slideClassName} />
{/* Childrens */}
<>
<span className={_checkedChildrenClassName}>
{checkedChildren || null}
</span>
<span className={_uncheckedChildrenClassName}>
{uncheckedChildren || null}
</span>
</>
</span>
</span>
);
}
As you structure is ready, the last major thing pending is listening to the change event and updating the state, so lets do that.
handleChange = e => {
const { checked } = e.target;
const { name, onChange } = this.props;
this.setState({ checked });
onChange && onChange({ name, checked });
};
In this we are updating our inner state as well as the returning the value to the parent.
If this is a controlled component then parent component will return the checked status to us, as we are already maintaining the inner state we need to check that the status we are receiving in the props is different than the previous one then only update the state, else do nothing.
componentDidUpdate(prevProps) {
if (!isEqual(prevProps.checked, this.props.checked)) {
this.setState({
checked: this.props.checked
});
}
}
Complete code of toggle switch in react.
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";
import { isEqual } from "lodash";
class ToggleSwitch extends Component {
static propTypes = {
name: PropTypes.string,
defaultChecked: PropTypes.bool,
onChange: PropTypes.func,
checked: PropTypes.bool,
className: PropTypes.string,
rounded: PropTypes.bool,
variant: PropTypes.oneOf(["primary", "success", "danger"]),
/* Children to show on active state */
checkedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
/* Children to show on inactive state */
uncheckedChildren: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.element
]),
innerRef: PropTypes.instanceOf(Element)
};
static defaultProps = {
defaultChecked: false,
checked: false,
variant: "primary"
};
constructor(props) {
super(props);
const { checked, defaultChecked } = this.props;
this.state = {
checked: defaultChecked || checked || false
};
}
handleChange = e => {
const { checked } = e.target;
const { name, onChange } = this.props;
this.setState({ checked });
onChange &&
onChange({
name,
checked
});
};
componentDidUpdate(prevProps) {
if (!isEqual(prevProps.checked, this.props.checked)) {
this.setState({
checked: this.props.checked
});
}
}
render() {
const { checked } = this.state;
const {
rounded,
checkedChildren,
uncheckedChildren,
className,
name,
variant,
innerRef
} = this.props;
const _className = cx(styles.wrapper, className);
const _slideClassName = cx(styles.slider, styles[variant], {
[styles.round]: rounded
});
const _checkedChildrenClassName = cx(styles.children, styles.checked, {
[styles.visible]: checked
});
const _uncheckedChildrenClassName = cx(styles.children, styles.unchecked, {
[styles.visible]: !checked
});
return (
<span className={_className}>
<span className={styles.switch}>
<input
type="checkbox"
checked={this.state.checked}
onChange={this.handleChange}
name={name}
ref={innerRef}
/>
{/* Overlay */}
<span className={_slideClassName} />
{/* Childrens */}
<>
<span className={_checkedChildrenClassName}>
{checkedChildren || null}
</span>
<span className={_uncheckedChildrenClassName}>
{uncheckedChildren || null}
</span>
</>
</span>
</span>
);
}
}
export default React.forwardRef((props, ref) => {
return <ToggleSwitch {...props} innerRef={ref} />;
});
Complete CSS code of toggle switch.
Styling is one of the major part of this component as we are using only CSS to create the toggle switch.
We will be using sibling and pseudo selector to toggle the slider. Also we will be providing different variants of our switch.
//index.module.css
.wrapper {
display: inline-flex;
margin: 0 10px;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 100%;
height: 100%;
z-index: 1;
position: relative;
cursor: pointer;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: 0.4s;
transition: 0.4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: 0.4s;
transition: 0.4s;
}
/* Different variants*/
input:checked + .slider.primary {
background-color: #2196f3;
}
input:focus + .slider.primary {
box-shadow: 0 0 1px #2196f3;
}
input:checked + .slider.danger {
background-color: #ff5722;
}
input:focus + .slider.danger {
box-shadow: 0 0 1px #ff5722;
}
input:checked + .slider.success {
background-color: #7cb342;
}
input:focus + .slider.success {
box-shadow: 0 0 1px #7cb342;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/*Childrens style */
.children {
position: absolute;
top: 4px;
height: 26px;
width: 26px;
bottom: 4px;
text-align: center;
line-height: 26px;
transition: 0.12s;
opacity: 0;
}
.checked {
left: 2px;
}
.unchecked {
right: 2px;
}
.visible {
opacity: 1;
}
Input
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import ToggleSwitch from "./Components/ToggleSwitch";
import * as serviceWorker from "./serviceWorker";
import { CheckOutlined, CloseOutlined } from "@ant-design/icons";
ReactDOM.render(
<div className="abc">
<ToggleSwitch name="abc" rounded={true} />
<ToggleSwitch name="abc" variant="success" />
<ToggleSwitch
name="abc"
rounded={true}
variant="danger"
defaultChecked={true}
checkedChildren={<CheckOutlined />}
uncheckedChildren={<CloseOutlined />}
/>
<ToggleSwitch
name="abc"
defaultChecked={true}
variant="danger"
checkedChildren={<CheckOutlined />}
uncheckedChildren={<CloseOutlined />}
/>
</div>,
document.getElementById("root")
);
Output
