create-a-toggle-switch-in-react-as-a-reusable-component

In this article, we’re going to create an iOS-inspired toggle switch using React components. By the end, we’ll have built a simple demo React App that uses our custom toggle switch component.

We could use third-party libraries for this, but building from scratch allows us to better understand how our code is working and allows us to customize our component completely.

Forms provide a major means for enabling user interactions. The checkbox is traditionally used for collecting binary data — such as yes or no, true or false, enable or disable, on or off, etc. Although some modern interface designs steer away from form fields when creating toggle switches, I’ll stick with them here due to their greater accessibility.

Here’s a screenshot of the component we’ll be building:

The final result

Getting Started

We can start with a basic HTML checkbox input form element with its necessary properties set:


To build around it, we might need an enclosing

with a class, a and the control itself. Adding everything, we might get something like this:

In time, we can get rid of the label text and use the tag to check or uncheck the checkbox input control. Inside the , let’s add two s that help us construct the switch holder and the toggling switch itself:

Converting to a React Component

Now that we know what needs to go into the HTML, all we need to do is to convert the HTML into a React component. Let’s start with a basic component here. We’ll make this a class component, and then we’ll convert it into hooks, as it’s easier for new developers to follow state than useState:

import React, { Component } from "react";

class ToggleSwitch extends Component {
  render() {
    return (
      
); } } export default ToggleSwitch;

At this point, it’s not possible to have multiple toggle switch sliders on the same view or same page due to the repetition of ids. We could leverage React’s way of componentization here, but in this instance, we’ll be using props to dynamically populate the values:

import React, { Component } from "react";

class ToggleSwitch extends Component {
  render() {
    return (
      
); } } export default ToggleSwitch;

The this.props.Name will populate the values of id, name and for (note that it is htmlFor in React JS) dynamically, so that you can pass different values to the component and have multiple of them on the same page. Also, the tag doesn’t have an ending tag; instead it’s closed in the starting tag like , and this is completely fine.

Styling and CSS

I recently wrote 8 Ways to Style React Components, and we’ll use SCSS here (which I consider the best way). Since our SCSS file is already included through the starting index.js script, we don’t need to include another SCSS file again in the component itself. Let’s first have a look how the base CSS is done. After that, we’ll make improvements on it. A few things that we’ll be doing with the styling are as follows:

  • By default, the switch is going to be only 75px wide and vertically aligned inline-block so that it’s inline with the text and doesn’t cause layout problems.
  • We’ll make sure that the control is not selectable so that users can drag and drop it.
  • We’ll also be hiding the original checkbox input.
  • Both the ::after and ::before needs to be styled and made as elements to get them into the DOM and style it.
  • We’ll also add some CSS transitions to make it look cool and animated.
.toggle-switch {
  position: relative;
  width: 75px;
  display: inline-block;
  vertical-align: middle;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  text-align: left;
}
.toggle-switch-checkbox {
  display: none;
}
.toggle-switch-label {
  display: block;
  overflow: hidden;
  cursor: pointer;
  border: 0 solid #ccc;
  border-radius: 20px;
  margin: 0;
}
.toggle-switch-inner {
  display: block;
  width: 200%;
  margin-left: -100%;
  transition: margin 0.3s ease-in 0s;
}
.toggle-switch-inner::before, .toggle-switch-inner::after {
  display: block;
  float: left;
  width: 50%;
  height: 34px;
  padding: 0;
  line-height: 34px;
  font-size: 14px;
  color: white;
  font-weight: bold;
  box-sizing: border-box;
}
.toggle-switch-inner:before {
  content: "Yes";
  text-transform: uppercase;
  padding-left: 10px;
  background-color: #f90;
  color: #fff;
}
.toggle-switch-disabled {
  background-color: #ccc;
  cursor: not-allowed;
}
.toggle-switch-disabled::before {
  background-color: #ccc;
  cursor: not-allowed;
}
.toggle-switch-inner::after {
  content: "No";
  text-transform: uppercase;
  padding-right: 10px;
  background-color: #ccc;
  color: #fff;
  text-align: right;
}
.toggle-switch-switch {
  display: block;
  width: 24px;
  margin: 5px;
  background: #fff;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 40px;
  border: 0 solid #ccc;
  border-radius: 20px;
  transition: all 0.3s ease-in 0s;
}
.toggle-switch-checkbox:checked   .toggle-switch-label .toggle-switch-inner {
  margin-left: 0;
}
.toggle-switch-checkbox:checked   .toggle-switch-label .toggle-switch-switch {
  right: 0px;
}

If we look at this, the "Yes" and "No" can be sent to here dynamically from the control using data-* attributes in HTML5. This is required, because it’s not always the same "Yes" and "No" values. Let’s make it dynamic for now:

.toggle-switch-inner::before {
  content: attr(data-yes);
  /* other styles */
}
.toggle-switch-inner::after {
  content: attr(data-no);
  /* other styles */
}

Also, it would be a great idea to use a smaller version of switch, without the text, so let’s add some more CSS for it with some minimal sizes and removing the text:

.toggle-switch.small-switch {
  width: 40px;
}
.toggle-switch.small-switch .toggle-switch-inner:after, .toggle-switch.small-switch .toggle-switch-inner:before {
  content: "";
  height: 20px;
  line-height: 20px;
}
.toggle-switch.small-switch .toggle-switch-switch {
  width: 16px;
  right: 20px;
  margin: 2px;
}

With respect to responsiveness, we should be changing the complete size. For a hacky version, we’re going to use the CSS scale function. Here we’ve covered all the Bootstrap-based responsive widths of devices:

@media screen and (max-width: 991px) {
  .toggle-switch {
    transform: scale(0.9);
  }
}
@media screen and (max-width: 767px) {
  .toggle-switch {
    transform: scale(0.825);
  }
}
@media screen and (max-width: 575px) {
  .toggle-switch {
    transform: scale(0.75);
  }
}

Converting the above code to SCSS, we’ll get something like:

.toggle-switch {
  position: relative;
  width: 75px;
  display: inline-block;
  vertical-align: middle;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  text-align: left;
  &-checkbox {
    display: none;
  }
  &-label {
    display: block;
    overflow: hidden;
    cursor: pointer;
    border: 0 solid #ccc;
    border-radius: 20px;
    margin: 0;
  }
  &-inner {
    display: block;
    width: 200%;
    margin-left: -100%;
    transition: margin 0.3s ease-in 0s;
    &:before,
    &:after {
      display: block;
      float: left;
      width: 50%;
      height: 34px;
      padding: 0;
      line-height: 34px;
      font-size: 14px;
      color: white;
      font-weight: bold;
      box-sizing: border-box;
    }
    &:before {
      content: attr(data-yes);
      text-transform: uppercase;
      padding-left: 10px;
      background-color: #f90;
      color: #fff;
    }
  }
  &-disabled {
    background-color: #ccc;
    cursor: not-allowed;
    &:before {
      background-color: #ccc;
      cursor: not-allowed;
    }
  }
  &-inner:after {
    content: attr(data-no);
    text-transform: uppercase;
    padding-right: 10px;
    background-color: #ccc;
    color: #fff;
    text-align: right;
  }
  &-switch {
    display: block;
    width: 24px;
    margin: 5px;
    background: #fff;
    position: absolute;
    top: 0;
    bottom: 0;
    right: 40px;
    border: 0 solid #ccc;
    border-radius: 20px;
    transition: all 0.3s ease-in 0s;
  }
  &-checkbox:checked   &-label {
    .toggle-switch-inner {
      margin-left: 0;
    }
    .toggle-switch-switch {
      right: 0px;
    }
  }
  &.small-switch {
    width: 40px;
    .toggle-switch-inner {
      &:after,
      &:before {
        content: "";
        height: 20px;
        line-height: 20px;
      }
    }
    .toggle-switch-switch {
      width: 16px;
      right: 20px;
      margin: 2px;
    }
  }
  @media screen and (max-width: 991px) {
    transform: scale(0.9);
  }
  @media screen and (max-width: 767px) {
    transform: scale(0.825);
  }
  @media screen and (max-width: 575px) {
    transform: scale(0.75);
  }
}

Theming in SCSS

Since we can use variables in SCSS, theming becomes easier. Adding support for multiple color themes in our app is made easier using SCSS. Sass Theming: The Neverending Story explains some of it. We’ll be using some color themes here and change all the raw colors to variables. The first three lines are a configurable set of colors, which helps us theme our little control:

// Colors
$label-color: #ccc;
$toggle-color: #f90;
$white: #fff;

// Styles
.toggle-switch {
  position: relative;
  width: 75px;
  display: inline-block;
  vertical-align: middle;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  text-align: left;
  &-checkbox {
    display: none;
  }
  &-label {
    display: block;
    overflow: hidden;
    cursor: pointer;
    border: 0 solid $label-color;
    border-radius: 20px;
    margin: 0;
  }
  &-inner {
    display: block;
    width: 200%;
    margin-left: -100%;
    transition: margin 0.3s ease-in 0s;
    &:before,
    &:after {
      display: block;
      float: left;
      width: 50%;
      height: 34px;
      padding: 0;
      line-height: 34px;
      font-size: 14px;
      color: white;
      font-weight: bold;
      box-sizing: border-box;
    }
    &:before {
      content: attr(data-yes);
      text-transform: uppercase;
      padding-left: 10px;
      background-color: $toggle-color;
      color: $white;
    }
  }
  &-disabled {
    background-color: $label-color;
    cursor: not-allowed;
    &:before {
      background-color: $label-color;
      cursor: not-allowed;
    }
  }
  &-inner:after {
    content: attr(data-no);
    text-transform: uppercase;
    padding-right: 10px;
    background-color: $label-color;
    color: $white;
    text-align: right;
  }
  &-switch {
    display: block;
    width: 24px;
    margin: 5px;
    background: $white;
    position: absolute;
    top: 0;
    bottom: 0;
    right: 40px;
    border: 0 solid $label-color;
    border-radius: 20px;
    transition: all 0.3s ease-in 0s;
  }
  &-checkbox:checked   &-label {
    .toggle-switch-inner {
      margin-left: 0;
    }
    .toggle-switch-switch {
      right: 0px;
    }
  }
  &.small-switch {
    width: 40px;
    .toggle-switch-inner {
      &:after,
      &:before {
        content: "";
        height: 20px;
        line-height: 20px;
      }
    }
    .toggle-switch-switch {
      width: 16px;
      right: 20px;
      margin: 2px;
    }
  }
  @media screen and (max-width: 991px) {
    transform: scale(0.9);
  }
  @media screen and (max-width: 767px) {
    transform: scale(0.825);
  }
  @media screen and (max-width: 575px) {
    transform: scale(0.75);
  }
}

Interactions and JavaScript

Now let’s concentrate on getting the basic component working. Since everything in React happens on the fly, we need to use states for storing local component information. Just a friendly reminder: whenever you change any value in the state of a component, the render() life cycle method of React JS will be fired. Keeping that in mind, let’s build our default state:

state = {
  checked: this.props.defaultChecked
};

All we require is just the checked state. It’s going to be a boolean value and it will be received from a defaultChecked prop of the component. We’ll be using a static defaultProps in our class component as a fallback. This looks something similar to the following code:

// Set text for rendering.
static defaultProps = {
  Text: ["Yes", "No"]
}

Since most of the props have to be user set and we can’t use arbitrary values, it’s always better to stop rendering if the required props aren’t passed on. This can be done using a simple JavaScript if statement or a ternary operator using ? : or a short-circuited “and” &&:

{this.props.id ? (
  
) : null}

We might as well add the event listeners using the props for the component like onChange. Along with that, we can also add few more props for the following:

  • id (required): this is the id that’s going to be passed to the checkbox input control. Without this, the component won’t render.
  • Text (required): if you aren’t using the Small version of the control, you might need to pass this to the Toggle Switch as an array of two values, which signify the text for True and False. An example would be Text={["Yes", "No"]}.
  • Name (optional): this will be label text of the checkbox input, but we generally won’t be using this.
  • onChange (optional): this will be directly passed to the .
  • defaultChecked (optional): this will be directly passed to the .
  • Small (optional): this is a boolean value, which renders the Toggle Switch in a small mode, where the text isn’t rendered.
  • currentValue (optional): this will be directly passed to the as defaultValue.
  • disabled (optional): this will be directly passed to the .

As our app grows, we can catch a lot of bugs with type checking. React has some built-in type-checking abilities. To run type checking on the props for a component, you can assign the special propTypes property. We can enforce the above list of props using React’s PropType library, which is a separate library that exports a range of validators that can be used to make sure the data you receive is valid.

In this example, we’re using the following:

  • PropTypes.string.isRequired: this is a string value and it’s required and mandatory.
  • PropTypes.string: this is a string value but it isn’t mandatory.
  • PropTypes.func: this is a prop that takes in a function as value but it isn’t mandatory.
  • PropTypes.bool: this is a boolean value but it isn’t mandatory.

You need to import the PropTypes library using:

import PropTypes from "prop-types";

And after the class definition and before the export statement, if you have it separately, we’ll define the PropTypes in the following way.

ToggleSwitch.propTypes = {
  id: PropTypes.string.isRequired,
  Text: PropTypes.string.isRequired,
  Name: PropTypes.string,
  onChange: PropTypes.func,
  defaultChecked: PropTypes.bool,
  Small: PropTypes.bool,
  currentValue: PropTypes.bool,
  disabled: PropTypes.bool
};

With all the above said elements, our component now looks like this:

import React, { Component } from "react";
import PropTypes from "prop-types";

/*
Toggle Switch Component
Note: id is required for ToggleSwitch component to function. Name, currentValue, defaultChecked, Small and onChange're optional.
Usage: 
*/

class ToggleSwitch extends Component {
  state = {
    checked: this.props.defaultChecked
  };
  onChange = e => {
    this.setState({
      checked: e.target.checked
    });
    if (typeof this.props.onChange === "function") this.props.onChange();
  };
  render() {
    return (
      
{this.props.id ? ( ) : null}
); } // Set text for rendering. static defaultProps = { Text: ["Yes", "No"] }; } ToggleSwitch.propTypes = { id: PropTypes.string.isRequired, Text: PropTypes.string.isRequired, Name: PropTypes.string, onChange: PropTypes.func, defaultChecked: PropTypes.bool, Small: PropTypes.bool, currentValue: PropTypes.bool, disabled: PropTypes.bool }; export default ToggleSwitch;

Unit Testing

Any code that has been created needs to be unit tested, at least to a basic level. In my current workplace, we’re using a snapshot based unit testing. We’ll be checking for the following conditions in the component. We’re using the library enzyme by AirBNB for testing purposes and jest as the default test runner. By writing these tests, we’re making sure our Toggle Switch component:

  • should render without crashing
  • should match the snapshot
  • should fail if the ID isn’t supplied
  • should render if the ID is supplied
  • should disable the switch slider if it contains disabled props

Here’s the complete ToggleSwitch.test.js:

import React from "react";
import { shallow } from "enzyme";
import ToggleSwitch from "../components/ToggleSwitch";

const text = ["Yes", "No"];
const chkID = "checkboxID";

describe("Toggle Switch Component", () => {
  it("should render without crashing", () => {
    const ToggleSwitchComponent = shallow();
    expect(ToggleSwitchComponent.html()).not.toHaveLength(0);
  });

  it("should match snapshot", () => {
    const ToggleSwitchComponent = shallow();
    expect(ToggleSwitchComponent).toMatchSnapshot();
  });

  it("should fail if id is not supplied", () => {
    const ToggleSwitchComponent = shallow();
    expect(ToggleSwitchComponent.find("label")).toHaveLength(0);
  });

  it("should render if id is supplied", () => {
    const ToggleSwitchComponent = shallow(
      
    );
    expect(ToggleSwitchComponent.find("label")).not.toHaveLength(0);
  });

  it("should disable switch slider if it contains disabled props", () => {
    const ToggleSwitchComponent = shallow(
      
    );
    expect(ToggleSwitchComponent.find("#"   chkID).props().disabled).toBe(true);
  });
});

Summary

You can get the complete source code from praveenscience/ToggleSwitch: Implementing a Toggle Switch in React JS as a Reusable Component. The usage instructions are updated in the GitHub Repository.

I hope you’ve found this article useful. If you have any comments or questions, please hit me up on Twitter.

Praveen is a software and web developer, cloud computing consultant, full-stack developer, UX architect, a CEO, and even … a cook. You can find him at praveen.science.

409 comments

  1. I just like the helpful info you supply on your articles. I’ll bookmark your weblog and take a look at again right here regularly. I am rather sure I’ll be told lots of new stuff right right here! Good luck for the following!|

  2. Neat blog! Is your theme custom made or did you download it from somewhere? A design like yours with a few simple adjustements would really make my blog shine. Please let me know where you got your theme. Thanks a lot|

  3. You really make it appear really easy along with your presentation but I to find this topic to be really one thing which I think I would by no means understand. It kind of feels too complex and extremely large for me. I’m looking forward to your next put up, I’ll attempt to get the grasp of it!|

  4. I was very happy to uncover this website. I wanted to thank you for ones time due to this fantastic read!! I definitely loved every little bit of it and I have you book marked to see new stuff in your web site.|

  5. With havin so much content and articles do you ever run into any issues of plagorism or copyright violation? My website has a lot of exclusive content I’ve either authored myself or outsourced but it appears a lot of it is popping it up all over the web without my permission. Do you know any techniques to help prevent content from being ripped off? I’d truly appreciate it.|

  6. We stumbled over here coming from a different page and thought I might as well check things out. I like what I see so now i’m following you. Look forward to looking into your web page repeatedly.|

  7. I’ve been surfing on-line more than three hours lately, yet I by no means found any fascinating article like yours. It is beautiful price sufficient for me. In my view, if all site owners and bloggers made excellent content material as you probably did, the net shall be a lot more helpful than ever before.|

  8. Thank you for any other excellent article. The place else could anyone get that kind of information in such a perfect way of writing? I’ve a presentation subsequent week, and I am on the search for such info.|

  9. That is really attention-grabbing, You’re an overly professional blogger. I have joined your rss feed and look ahead to in quest of more of your great post. Also, I’ve shared your website in my social networks|

  10. I’d like to thank you for the efforts you have put in writing this blog. I’m hoping to view the same high-grade content from you in the future as well. In fact, your creative writing abilities has encouraged me to get my own, personal website now ;)|

  11. This blog is disseminating valuable information to people who are most concerned of the following issues being targeted by this site. Many certainly will keep coming back to check out updated posts.

  12. I don’t even know how I ended up here, but I thought this
    post was good. I don’t know who you are but certainly
    you are going to a famous blogger if you aren’t already 😉 Cheers!

  13. Howdy! I know this is kind of off topic but I was wondering which blog platform are you using for this site?
    I’m getting sick and tired of WordPress because I’ve had problems with hackers and I’m
    looking at options for another platform. I would
    be great if you could point me in the direction of a good platform.

  14. Amazing! This blog looks just like my old one!

    It’s on a totally different subject but it has pretty much the same page layout and design. Excellent choice
    of colors!

  15. I’m impressed, I must say. Rarely do I encounter a
    blog that’s equally educative and amusing, and
    let me tell you, you have hit the nail on the head. The issue is an issue
    that too few folks are speaking intelligently about.

    I am very happy I came across this in my hunt for something concerning this.

  16. Its like you read my mind! You seem to know a lot about this, like you wrote the
    book in it or something. I think that you could do with
    a few pics to drive the message home a bit, but instead
    of that, this is fantastic blog. A great read.
    I’ll certainly be back.

  17. Hello my friend! I wish to say that this post is amazing,
    great written and come with almost all significant infos.
    I’d like to peer extra posts like this .

  18. Hello, everything is going nicely here and ofcourse every one is sharing information, that’s genuinely fine, keep up writing.

  19. It’s going to be end of mine day, but before ending I am reading this impressive article to increase
    my know-how.

  20. I have to thank you for the efforts you have put in writing this website.
    I am hoping to see the same high-grade content from you in the future as well.
    In truth, your creative writing abilities has encouraged me to get my own blog now 😉

  21. When I initially commented I clicked the “Notify me when new comments are added” checkbox and
    now each time a comment is added I get four emails with the same
    comment. Is there any way you can remove people from
    that service? Appreciate it!

  22. I have been exploring for a bit for any high quality articles or weblog
    posts on this sort of area . Exploring in Yahoo I at last stumbled upon this
    website. Reading this info So i am happy to convey that I have a very excellent uncanny feeling I found out exactly what I needed.
    I such a lot for sure will make sure to do not forget
    this website and provides it a look on a constant basis.

  23. Hey! I’m at work surfing around your blog from my new iphone 3gs!
    Just wanted to say I love reading your blog and look
    forward to all your posts! Keep up the fantastic work!

  24. I’m not sure where you’re getting your info, but good topic.
    I needs to spend some time learning much more or understanding more.
    Thanks for fantastic information I was looking for this info for my mission.

  25. I’m truly enjoying the design and layout of your blog.
    It’s a very easy on the eyes which makes it much more pleasant for
    me to come here and visit more often. Did you hire out a developer to create
    your theme? Superb work!

  26. Why people still use to read news papers when in this technological globe everything
    is accessible on net?

  27. I’m not sure why but this web site is loading extremely slow for me.
    Is anyone else having this issue or is it a problem on my end?
    I’ll check back later and see if the problem still exists.

  28. This page definitely has all the information and facts I wanted about this subject and didn’t know who to ask.

  29. Quality articles or reviews is the key to interest the visitors to pay
    a visit the site, that’s what this web page is
    providing.

  30. Excellent way of explaining, and nice article to get facts about my presentation subject,
    which i am going to convey in college.

  31. Pingback: zithromax otc
  32. Pingback: cialis canada 20mg
  33. Pingback: order viagra 25mg
  34. Pingback: buy insurance
  35. Pingback: tx payday loans
  36. Pingback: 20 mg cost
  37. Pingback: 20 mg cost
  38. Pingback: womens viagra
  39. Pingback: buy sildenafil 20
  40. Pingback: cialis walmart
  41. Pingback: viagra pills
  42. Pingback: clonidine patch
  43. Pingback: carvedilol er
  44. Pingback: cialis 20 mg
  45. Pingback: levitra 20 mg cost
  46. Pingback: best viagra prices
  47. Pingback: cialis substitute
  48. Pingback: cialis daily pill
  49. Pingback: cialis pill
  50. Pingback: viagra best buy
  51. Pingback: tadalafil goodrx
  52. Pingback: cialis cost usa
  53. Pingback: viagra results
  54. Pingback: cialis free sample
  55. Pingback: Anonymous
  56. Pingback: Anonymous
  57. Pingback: Anonymous
  58. Pingback: ivermectin kaufen
  59. Pingback: stromectol price
  60. Pingback: cialis kaufen
  61. Pingback: ivermectin 200mg
  62. Pingback: tadalalafilaquest
  63. Pingback: stromectol order
  64. Pingback: tadalafil sale
  65. Pingback: ivermectin 8 mg
  66. Pingback: sildenafil tablets
  67. Pingback: cheap cialis india
  68. Pingback: tadalafil generic
  69. Pingback: calis
  70. Pingback: merck covid drug
  71. Pingback: buy generic cialis
  72. Pingback: slots
  73. Pingback: prednisone
  74. Pingback: generic cialis
  75. Pingback: viagra on line
  76. Pingback: cialis tablets
  77. Pingback: borgata casino pa
  78. Pingback: ivermectin wormer
  79. Pingback: real casino slots
  80. Pingback: cialis daily
  81. Pingback: cheap cialis india
  82. Pingback: cialis goodrx
  83. Pingback: cost of ivermectin
  84. Pingback: cephalexin kids
  85. Pingback: cephalexin ckd
  86. Pingback: keflex ssti
  87. Pingback: stromectol
  88. Pingback: ivermectin nahdi
  89. Pingback: lucky land casino
  90. Pingback: ivermectin 0.1
  91. Pingback: ivermectin drug
  92. Pingback: ivermectin 20 mg
  93. Pingback: stromectol kaufen

Leave a Reply

Your email address will not be published.