Create checkbox in react

Learn how to create your own custom checkbox component in react.

This component will behave as the checkbox and we will style it little to make it interactive.

Following is the list of props we expect in our component.

option: PropTypes.shape({
    value: PropTypes.any,
    label: PropTypes.node,
    disabled: PropTypes.bool
  }),
  name: PropTypes.string.isRequired,
  onChange: PropTypes.func,
  className: PropTypes.string,
  inputClassName: PropTypes.string,
  /* Applies to the label of each item in this group */
  labelClassName: PropTypes.string,
  checkedItemLabelClassName: PropTypes.string,
  /* Applies to custom label for checkbox */
  customLabelClassName: PropTypes.string,
  value: PropTypes.bool,
  defaultChecked: PropTypes.bool,
  innerRef: PropTypes.any

These are some generic props which is sufficient for this type of components but you can extend it as per your need.

In option we are passing all the required items we want because this way in case we want to create a group of checkboxes we can create an array of these objects and do it. We see how to create it later.

Our component will be composed of three different parts.

  • Label: label of the checkbox.
  • Input: Input type checkbox. This component will be invisible.
  • Proxy Checkbox: An overlay over the checkbox which will enhance the design.

So let us start creating the checkbox component in react.

We will be using some extra packages for our help.

  • prop-types: To validate the props we are going to recieve.
  • classnames:- It will help us to use CSS classes as javascript objects. You just have to name the CSS file as index.module.css.

React checkbox component folder structure

First, start by importing all the required packages.

import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

const Checkbox = (props, ref) => {
  //Other codes will go here
}

export default React.forwardRef((props, ref) => Checkbox(props, ref));

As we are not going to maintain any state that is why we are creating a functional component.

We are forwarding ref from the parent and passing to the component to have extra control.

Now let’s create the classes for each different parts of our component and assign them. As we can use them as javascript objects with classnames package. We can dynamically add classes based on the Boolean value.

const {
    className,
    labelClassName,
    name,
    option,
    value,
    defaultChecked,
    inputClassName,
    checkedItemLabelClassName,
    customLabelClassName
  } = props;

  const _className = cx(className, styles.item, {
    [styles.disabled]: option.disabled
  });

  const _inputClassName = cx(styles.checkbox, inputClassName);

  const _labelClassName = cx(labelClassName, styles.label, {
    [checkedItemLabelClassName]: value
  });

  const _customLabelClassName = cx(customLabelClassName, styles.customLabel);

  return (
    <div className={_className}>
      <input
        disabled={option.disabled}
        type="checkbox"
        name={name}
        id={`${name}-${option.value}`}
        value={option.value}
        defaultChecked={defaultChecked}
        checked={value}
        onChange={handleChange}
        className={_inputClassName}
        ref={ref}
      />

      <label
        className={_customLabelClassName}
        htmlFor={`${name}-${option.value}`}
      ></label>

      <label htmlFor={`${name}-${option.value}`} className={_labelClassName}>
        {option.label}
      </label>
    </div>
  );

We can pass custom classes for each part and update the styling as per our need.

Now lets add a function to handle the event. We will return the event value to the parent.

const handleChange = e => {
  const { name, onChange, option } = props;
  const { checked } = e.target;
  const {value } = option;

  if (onChange) {
    onChange({
      name,
      value,
      checked
    });
  }
};

Complete code of checkbox component in react

import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import styles from "./index.module.css";

const Checkbox = (props, ref) => {
  const handleChange = e => {
    const { name, onChange, option } = props;
    const { checked } = e.target;
    const {value } = option;

    if (onChange) {
      onChange({
        name,
        value,
        checked
     });
    }
  };

  const {
    className,
    labelClassName,
    name,
    option,
    value,
    defaultChecked,
    inputClassName,
    checkedItemLabelClassName,
    customLabelClassName
  } = props;

  const _className = cx(className, styles.item, {
    [styles.disabled]: option.disabled
  });

  const _inputClassName = cx(styles.checkbox, inputClassName);

  const _labelClassName = cx(labelClassName, styles.label, {
    [checkedItemLabelClassName]: value
  });

  const _customLabelClassName = cx(customLabelClassName, styles.customLabel);

  return (
    <div className={_className}>
      <input
        disabled={option.disabled}
        type="checkbox"
        name={name}
        id={`${name}-${option.value}`}
        value={option.value}
        defaultChecked={defaultChecked}
        checked={value}
        onChange={handleChange}
        className={_inputClassName}
        ref={ref}
      />

      <label
        className={_customLabelClassName}
        htmlFor={`${name}-${option.value}`}
      ></label>

      <label htmlFor={`${name}-${option.value}`} className={_labelClassName}>
        {option.label}
      </label>
    </div>
  );
};

Checkbox.propTypes = {
  option: PropTypes.shape({
    value: PropTypes.any,
    label: PropTypes.node,
    disabled: PropTypes.bool
  }),

  name: PropTypes.string.isRequired,

  onChange: PropTypes.func,

  className: PropTypes.string,

  inputClassName: PropTypes.string,

  /* Applies to the label of each item in this group */
  labelClassName: PropTypes.string,

  checkedItemLabelClassName: PropTypes.string,

  /* Applies to custom label for checkbox */
  customLabelClassName: PropTypes.string,

  value: PropTypes.bool,

  defaultChecked: PropTypes.bool,

  innerRef: PropTypes.any
};

Checkbox.defaultProps = {
  checkedItemLabelClassName: ""
};

export default React.forwardRef((props, ref) => Checkbox(props, ref));

Now as our component structure is ready, let us style it.

We will be using sibling selector to add the overlay over the checkbox. Also state of the checkbox will also be updated by CSS only.

Complete CSS code of checkbox in react.

.checkbox {
  display: none;
}

.checkbox:checked + .customLabel {
  background: #68c721;
  border-color: #68c721;
}

.checkbox:checked + .customLabel:after {
  content: "";
}

.customLabel {
  border: 1px solid #a8acb1;
  background-color: #fff;
  border-radius: 3px;
  min-width: 20px;
  min-height: 20px;
  cursor: pointer;
}

.customLabel::after {
  width: 12px;
  height: 5px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  display: block;
  transform: rotate(-45deg) translateY(4px) translateX(-1px);
}

.white .checkbox:checked + .customLabel {
  background: transparent;
  border-color: #a8acb1;
}

.white .checkbox:checked + .customLabel::after {
  border-color: #2d2d2d;
}

.item {
  display: flex;
  align-items: center;
  margin: 0 10px 10px 10px;
}

.item.disabled {
  opacity: 0.3;
}

.item.disabled .label,
.item.disabled .customLabel {
  cursor: default;
}

.inlineItem {
  display: inline-flex;
}

.label {
  cursor: pointer;
  margin-left: 15px;
  font-size: 14px;
}

Input

ReactDOM.render(
  <div className="abc">
    <Checkbox
      option={{ label: "I am checked", value: "abc", disabled: false }}
      name="ck"
    />
    <Checkbox
      option={{ label: "I am not checked", value: "abc", disabled: false }}
      name="ck"
      defaultChecked={true}
    />
  </div>,
  document.getElementById("root")
);

Output

React Checkbox