ui > Inputs

Guide To React Inputs - Text, Checkbox, Radio

In this guide we're going to do a functional overview of the types of input elements that you can create with React. We'll be using the standard HTML <input> tag, starting from the default text input, and then moving onto other input types configured through the type prop. We'll be using React hooks and styled-components for easy styling and stateful functionality.

Note that when discussing these input "props", we're referencing the standard HTML attributes, and so as with all React props, they are the same as the HTML attributes except for callback functions like onChange which are camelCase. Feel free to reference HTML documentation for other standard attributes that can be set through props.

React Text Input Example with Hooks

import React, { useState } from 'react';
import styled from 'styled-components';
// Styling a regular HTML input
const StyledInput = styled.input`
display: block;
margin: 20px 0px;
border: 1px solid lightblue;
// Creating a custom hook
function useInput(defaultValue) {
const [value, setValue] = useState(defaultValue);
function onChange(e) {
return {
// Usage in App
function App() {
const inputProps = useInput();
return (
placeholder="Type in here"
<span>Value: {inputProps.value} </span>

React input value prop

The value prop is what determines the input's value. For text inputs, this is simply the current text value of the input, making it simple to understand when writing stateful logic. For checkboxes and radio buttons, it's the checked prop, as we describe below.

React input onChange prop

The onChange prop is a function that responds when the user interacts with the input. The browser tells us that a new value has been detected. However, we have not saved this value state anywhere in our application, so that's where we use hooks to create a "custom React hook", which is really just a function that returns the dynamic value/onChange props.

Using React hooks with inputs

So, the key is that we use the useState hook inside a helper method which we call useInput. This itself now becomes a "custom hook". Because all inputs share the same value and onChange props, this stateful functionality becomes reusable for any future inputs we want to create in our app.

We declare the onChange callback inside this hook, which uses the setValue callback given to us from React hooks. As a reminder, value and setValue are just names we created, as the useState hook doesn't require specific names for these attributes. We added an optional defaultValue argument we provide in case you would want to initialize the value (from data from a server for example) inside the useState call itself. So, now that we have our local input value and onChange props, we return those and then use the ...spread notation to place these props on our StyledInput.

Using checkboxes and radio buttons with React

Checkboxes and radio buttons use the checked property to determine their UI state (true or false), with their value being an arbitrary name for that selection. What can be confusing about this is that the checked HTML attribute here was intended to just set which checkbox/radio was set initially. The browser would keep track of when the user checked/unchecked inputs, not updating the checked attribute, and then when the user submitted a form, the browser would include the name/value props for the checked inputs automatically. We do things quite differently in React, where we are required to set the checked attribute, and watch for changes through onChange, and then keeping track of the state ourselves by examining e.target.checked.

const checkboxesList = [
'New Jersey',
const getDefaultCheckboxes = () =>
checkboxesList.map(checkbox => ({
name: checkbox,
checked: false,
export function useCheckboxes(defaultCheckboxes) {
const [checkboxes, setCheckboxes] = useState(
defaultCheckboxes || getDefaultCheckboxes(),
function setCheckbox(index, checked) {
const newCheckboxes = [...checkboxes];
newCheckboxes[index].checked = checked;
return {
const Checkbox = styled.input`
margin: 0px 10px 0px !important;
cursor: pointer;
const CheckboxLabel = styled.label`
cursor: pointer;
display: block;
font-weight: normal;
export function Checkboxes({ checkboxes, setCheckbox }) {
return (
{checkboxes.map((checkbox, i) => (
onChange={e => {
setCheckbox(i, e.target.checked);
export function CheckboxRadioExample() {
const checkboxes = useCheckboxes();
return (
<Checkboxes {...checkboxes} />
.filter(t => t.checked)
.map(checkbox => checkbox.name)
.join(', ')}

Checkbox Example with Hooks

Above you can see an example which initializes a list of checkboxes from a static list of US states. We map the list in getDefaultCheckboxes to create our initial state object array, which gets initialized when our useCheckboxes hook gets run at the beginning of our CheckboxRadioExample render method. We then name the array properties [checkboxes, setCheckboxes] as our state variable and setter.

From here, we have the custom hook return the list of checkboxes and a new callback setCheckbox which we create within the scope of the hook. The callback creates a copy of the current checkboxes list and sets the checked property of the passed index on the array. Then it sets the new array with the setCheckboxes setter.

At this point we have fully scoped the state logic of our checkbox list in the useCheckboxes hook. We're now free to use it in our app!

We render a list of CheckboxLabel (a <label> created by styled-components) and a <Checkbox type="checkbox"> (a styled.input). The type HTML attribute is what makes this a checkbox. We also add an onChange attribute here which we then access the e.target.checked (current checkbox state) from the event handler.

When someone interacts with a checkbox by mouse OR keyboard actions, (try tabbing and hitting spacebar on a checkbox), the onChange handler is fired, and we call our hook to update the list of checkboxes. The cycle is complete.

🛈 React School creates templates and video courses for building beautiful apps with React. Download our free Material UI template.