DEV Community

Cover image for Creating a Radio Button Component in React
MrScX
MrScX

Posted on

Creating a Radio Button Component in React

Introduction

Styling and working with radio buttons have always been tricky business. To custom style a boring default HTML radio button, you must put in a lot of CSS and have pretty advanced CSS knowledge as well. Manipulating CSS pseudo-selectors (::before/::after) and whatnot.

To add to that, using radio buttons in React can be even more tricky. Handling change, tracking which value is selected, having the radio button checked is not so obvious in React. We’ll create our custom RadioButton component in React, in which I’ll try to clarify all aspects of a radio button.

Setup

We’ll use create-react-app to set up a blank React project. Note: this tutorial assumes you have created React projects with create-react-app before and have NodeJS and npm installed. Also, install create-react-app using npm if you haven’t already. Run the following command in your terminal

create-react-app radio_button_tut
Enter fullscreen mode Exit fullscreen mode

That’ll create a folder called radio_button_tut and will initialize a new React app within with boilerplate code. Some of which we’ll remove. We’ll also install node-sass as we’ll be using SASS in our project. SASS can be used in React apps out of the box now without any additional setups or having to eject from create-react-app. Run the following command

npm install — save node-sass
Enter fullscreen mode Exit fullscreen mode

After this is done, open the project folder with your favorite code editor. Let’s start by removing the boilerplates created by create-react-app. Rename the App.css and index.css files to App.scss and index.scss. Nothing really changes here other than the extension to represent a SASS file. Remove everything inside App.scss and replace it with below codes

*, *:before, *:after {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

body{
  font-family: "Robot", sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  padding: 50px;
}
Enter fullscreen mode Exit fullscreen mode

It’s just some basic CSS resets and font setup. We also reset the pseudo-selectors, ::before and ::after otherwise the radio button styles will not take effect properly. Especially with centering the radio button selection circle. Try removing that code after we have created the CSS for the radio button.

Now, let’s create a new component inside the src folder. Create a folder named RadioButton. Inside create two more files, RadioButton.js and RadioButton.scss. Open the .scss file and copy-paste the below code.

@import url(https://fonts.googleapis.com/css?family=Roboto);

$md-radio-checked-color: rgb(51, 122, 183);
$md-radio-border-color: rgba(0, 0, 0, 0.54);
$md-radio-size: 20px;
$md-radio-checked-size: 10px; 
$md-radio-ripple-size: 15px;

@keyframes ripple {

  0% {
    box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.0);
  }

  50% { 
    box-shadow: 0px 0px 0px $md-radio-ripple-size rgba(0, 0, 0, 0.1);
  }

  100% {
    box-shadow: 0px 0px 0px $md-radio-ripple-size rgba(0, 0, 0, 0);
  }
}

.RadioButton {
    margin: 16px 0;

    input[type="radio"] {
        display: none;

        &:checked + label:before {
            border-color: $md-radio-checked-color;
            animation: ripple 0.2s linear forwards;   
        }

        &:checked + label:after {
            transform: scale(1);
        }
    }

    label {
        display: inline-block;
        height:$md-radio-size;
        position: relative;
        padding: 0 ($md-radio-size + 10px);
        margin-bottom: 0;
        cursor: pointer;
        vertical-align: bottom;

        &:before, &:after {
            position: absolute;            
            content: '';  
            border-radius: 50%;
            transition: all .3s ease;
            transition-property: transform, border-color;
        }

        &:before {
            left: 0;
            top: 0;
            width: $md-radio-size;
            height: $md-radio-size;
            border: 2px solid $md-radio-border-color;
        }

        &:after {
            top: $md-radio-size / 2 - $md-radio-checked-size / 2;
            left: $md-radio-size / 2 - $md-radio-checked-size / 2;
            width:$md-radio-checked-size;
            height:$md-radio-checked-size;
            transform: scale(0);
            background:$md-radio-checked-color;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, I didn’t write all these fancy CSS. I borrowed it from Hannes Kamecke on CodePen. The link to his pen => https://codepen.io/hansmaad/pen/zBdzxp

The CSS can seem complicated if your CSS game is not that strong. It uses some cool CSS features like KeyFrames for animations. The KeyFrame is used for the ripple effect on selecting the radio button. The ::before and, ::after are used in styling the radio button circle.

Open the RadioButton.js file now and paste in the following code

import React from "react";
import "./RadioButton.scss";

const RadioButton = (props) => {
    return (
        <div className="RadioButton">
            <input id={props.id} onChange={props.changed} value={props.value} type="radio" checked={props.isSelected} />
            <label htmlFor={props.id}>{props.label}</label>
        </div>
    );
}

export default RadioButton;
Enter fullscreen mode Exit fullscreen mode

It’s a simple functional React component. The JSX structure is very simple, a div is wrapping the radio input element and the label element. We pass 5 props to this component. The label, value, id, isSelected and, the changed props.

The changed props is a function. This function will fire every time the radio button is changed. In vanilla JS this would be the callback function we pass to the addEventListener. The event we’re listening for here is the onChange. You can see we’re assigning the props.changed to onChange event.

The value props is self-explanatory, it’s setting the value of the input element. This value is required to evaluate what is selected and what action should we take based on this selection. It’s even passed to databases on occasions when it’s necessary to save the value.

The label props is just the label text. This makes the component reusable across our application. As we’re not hardcoding anything.

isSelected is a boolean value. It controls which radio button should be selected. We dynamically evaluate this where we use the RadioButton component, based on the value of the radio input.

The id part is interesting. Without it, the setup here will not work. The id is mainly used by the label element. It needs to know for which input is this label for. If we don’t use this, no matter how hard you press the radio button, it’ll not be selected, meaning the event associated with it, onChange will never fire. Notice the htmlFor attribute. This attribute is named just for in plain HTML. But, as for is a keyword in JS, we can’t use that. React replaces that with htmlFor. During the build, it changes it to plain for. We set the id of the input field and tell the label about it with htmlFor. If the radio buttons are generated with for loops, the id can be the index of the loop.

That’s it for the RadioButton component. Let’s use it now. Dump the code below in App.js

import React, { Component } from "react";
import RadioButton from "./RadioButton/RadioButton";

class App extends Component {

    state = {
        paymentMethod: "COD"
    }

    radioChangeHandler = (event) => {

        this.setState({
            paymentMethod: event.target.value
        });
    }

    render() {

        const quickpay = this.state.paymentMethod === "QuickPay" ? <input type="text" placeholder="Enter transaction id"> : null;

        return (
            <div className="Apps">
                <div className="radio-btn-container" style={{ display: "flex" }}>

                    <RadioButton 
                        changed={ this.radioChangeHandler } 
                        id="1" 
                        isSelected={ this.state.paymentMethod === "QuickPay" } 
                        label="QuickPay" 
                        value="QuickPay" 
                    />

                    <RadioButton 
                        changed={ this.radioChangeHandler } 
                        id="2" 
                        isSelected={ this.state.paymentMethod === "COD" } 
                        label="Cash On Delivery" 
                        value="COD" 
                    />

                </div>

                { quickpay }

                <h2 style={{ marginTop: "50px" }}>
                    The selected radio button value is => { this.state.paymentMethod }
                </h2>
            </div>
        );
    }
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Ignore the inline styles here. They’re just for presentational purposes. One sets the margin and the other makes the radio buttons in a line (row) using FlexBox instead of columns.

The App component is a class component. So we can use states here and define event listeners that will be passed as props. The state is simple. It has a single property, paymentMethod. This is the value of the selected radio button. Initially, it’ll be COD as in cash on delivery. The setup here is of a fictional eCommerce website. Whereat checkout you'll be presented with payment options using radio buttons. Why radio buttons? Because you can select only one option as the payment method. You can either choose cash on delivery or QuickPay another fictional payment method. If you select QuickPay option, an input field will appear where you can enter the transaction id. When the other option is selected, the input will dissapear. We do this inside the render method in this line

const quickpay = this.state.paymentMethod === “QuickPay” ? <input type=”text” placeholder=”Enter transaction id”> : null;
Enter fullscreen mode Exit fullscreen mode

It checks the state, if the value is QuickPay meaning that option is selected, the variable will hold the input element or else null. This ensures we only see this when the appropriate option is selected.

We create 2 RadioButton components here and pass in the appropriate props. Notice the isSelected props. The value here evaluates to a boolean based on the value the state holds.

We pass down a changed props which is a function. This is the event listener. We create the radioChangeHandler method which is fired each time the radio button is switched. It sets the state to the value of the radio button using event.target.value. The event passed to this function is normal JavaScript. Nothing React specific. This is passed to all event listeners once they are fired, it contains information related to that event. When the state changes we see the effect it takes in our UI.

So that’s it! Hope it helped.

Oldest comments (3)

Collapse
 
alienpingu profile image
AlienPingu

Nice article!

Collapse
 
tanvibhakta_ profile image
Tanvi Bhakta

Hello! Great article! Would love if you could add a stackblitz/codesandbox to future articles :)

Collapse
 
codeclassifiers profile image
codeclassifiers

Quite useful tutorial. Thanks for sharing. Can you add a live demo/screenshot of final code for better code understanding :D