reusable input component react

Reusable Input Component in React Typescript [with useInput hook and error]

Author Profile
Abdul Basit OnLinkedIn

Every website has to deal with a form, and one of the most important and most used form elements is input. Today, we shall explore constructing a reusable input component in React using Typescript, we will also learn how to show errors and how to make it modular via useInput custom hook, and what are best UI UX practices to consider while creating an input component.

This comprehensive guide will equip you with the knowledge and skills to create an efficient and user-friendly input component for your web applications. In the first step, we will build components architecture and UI, and in the second step, we will work on functionality and see how to use it form.

Here is the design of our input component with and without error. The Codepen demo is also available at the end of the article.

React Input UI without Error

react login form ui

React Input UI with Error

react login form with error

Reusable Input Component Architecture

Let's talk about component architecture.

The input component has multiple props. It depends on your choice that what you want to keep and remove as required. The following are the possible props:

  • type: Specifies the type of input (e.g., 'text', 'number', 'email', 'password').

  • label: The text for the input label.

  • value: The current value of the input.

  • error: An error message to display which is optional and shown on the basis of condition.

  • name: Represents the name attribute of the input element to identify and group the data submitted from different input fields

  • placeholder: The placeholder text to display when the input is empty.

  • onChange: The function to handle changes in the input value. It takes an event of type ChangeEvent<HTMLInputElement> as a parameter.

  • disabled: which is optional (indicated by the ? symbol). It specifies whether the input should be disabled or not

JSX for React Input Component with label and error

import { ChangeEvent, FC } from 'react'
interface InputProps {
type: 'text' | 'number' | 'email' | 'password'
label: string
value: string | number
name: string
placeholder: string
error: boolean
disabled?: boolean
onChange: (e: ChangeEvent<HTMLInputElement>) => void
}
const Input: FC<InputProps> = ({
type,
label,
value,
name,
placeholder,
error,
disabled,
onChange,
}) => {
return (
<div className="input-wrapper">
<label htmlFor={label}>{label}</label>
<input
type={type}
id={label}
value={value}
name={name}
placeholder={placeholder}
onChange={onChange}
disabled={disabled}
/>
{error && <p className="error">Input filed can't be empty!</p>}
</div>
)
}
export default Input

CSS for Reusable Input Component

.input-wrapper {
margin-top: 20px;
}
/* Input style */
/* Label */
label {
font-weight: 600;
font-size: 18px;
color: #344054;
display: block;
margin-bottom: 8px;
}
/* Input field */
input {
font-size: 16px;
font-weight: 400;
width: 250px;
color: #344054;
background: #ffffff;
border: 1px solid #d0d5dd;
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
border-radius: 8px;
padding: 10px 14px;
}
input::placeholder {
font-size: 16px;
font-weight: 400;
color: #667085;
}
/* Input field focus */
input:focus {
}
.error {
color: #db4437;
font-size: 14px;
font-weight: 400;
margin-left: 12px;
margin-top: 4px;
}

This is how our input component UI will look like if we will render it in App or parent component

react reusable input component with typescript

We are done with component structure, architecture, and UI, now let's make it functional and see how we can use it in real world applications.

Become a Top 10% React Developer in 45 Days

Transform from an absolute beginner to a Top 10% React developer, get hired by a big tech company, and lead a React project.

All by dedicating 3 laser-focused hours a day for the next 3 months.

SAVE 10% with Coupon Code "FRIENDS10"

Start Learning Now

How to use Input Component with form

Let's say we are working on a login form in React App and want to use this input component there. In the below-given component, we import the Input component first and make two states, one for input value and the other for error.

While submitting the form, we check for an error and then show the error.

import React, { ChangeEvent, FormEvent, useState } from 'react'
import './App.css'
import Input from './components/Input'
const App: React.FC = () => {
const [name, setName] = useState('')
const [error, setError] = useState(false)
const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {
setName(e.target.value)
}
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!name.trim()) {
setError(true)
} else {
setError(false)
}
}
return (
<div className="page-center">
<form onSubmit={handleSubmit}>
<h3 className="form-title">Login Form</h3>
<Input
type="text"
label="Name"
value={name}
name="name"
error={error}
onChange={handleNameChange}
placeholder="Please enter your name"
/>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App

Note

This is the boilerplate code you can tweak it as per your need.

Best Practices for Creating Reusable Input Components in React

You can get most out of reusable input components with some best practices. You must consider these while working on forms or input in React APP.

So, let’s see what are these practices:

1. Call handleSubmit function in form

Always wrap input inside form and call submit function in form tag and make the submit button type="submit", it will invoke submit function both ways, by pressing submit button and enter key on the keyboard.

2. When to disable Input in React

Input tag also supports disabled prop, when to use it? It depends on the app use case, some developers make the input and submit form button both disable while submitting data to database to prevent retyping in input until you get the response back from database.

You can also learn more about how to use the loading spinner button in React, which I have discussed in this article.

3. Using htmlFor attribute for activating input by clicking a label

We also need to improve the UX of input component. There are two ways to activate input.

  1. Click the input box
  2. Click the label

We also have to implement the second point to improve the UX of the component.

The label element is associated with the input using the htmlFor attribute, which matches the id of the input element. This helps in accessibility by associating the label with the correct input.

4. useInput custom hook

Using the custom useInput hook we can further improve input component by making it modular and scalable.

import { ChangeEvent, useState } from 'react'
const useInput = (initialValue: string) => {
const [value, setValue] = useState(initialValue)
const [error, setError] = useState(false)
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
}
return {
value,
error,
onChange: handleChange,
setError,
}
}
export default useInput

How to use useInput hook in form;

import React, { FormEvent } from 'react'
import './App.css'
import Input from './components/Input'
import useInput from './hooks/useInput'
const App: React.FC = () => {
const { value, error, onChange, setError } = useInput('')
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!value.trim()) {
setError(true)
} else {
setError(false)
}
}
return (
<div className="page-center">
<form onSubmit={handleSubmit}>
<Input
type="text"
label="Name"
name="name"
value={value}
error={error}
onChange={onChange}
placeholder="Please enter your name"
/>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default App

What if you want to use two inputs in the form and that is email and password? If this is the case I will refactor the code this way;

import React, { FormEvent } from 'react'
import './App.css'
import Input from './components/Input'
import useInput from './hooks/useInput'
const App: React.FC = () => {
const emailInput = useInput('')
const passwordInput = useInput('')
const handleSubmit = (e: FormEvent) => {
e.preventDefault()
validateInput(emailInput)
validateInput(passwordInput)
console.log('email ==>', emailInput.value, 'password ==>', passwordInput.value)
}
const validateInput = (input: { value: string, setError: (value: boolean) => void }) => {
if (!input.value.trim()) {
input.setError(true)
} else {
input.setError(false)
}
}
return (
<div className="page-center">
<form onSubmit={handleSubmit}>
<h3 className="form-title">Login Form</h3>
<Input
type="email"
label="Email"
name="email"
placeholder="Please enter your email"
{...emailInput}
/>
<Input
type="password"
label="Password"
name="password"
placeholder="Please enter your password"
{...passwordInput}
/>
<button className="submit-btn">Submit</button>
</form>
</div>
)
}
export default App

While submitting the form we are checking if the input values are safely added to our state and verify the functionality of the useInput hook with console.log()

login form console data

Further Read

If you have a form with more than one input, there is one more pattern useForm custom hook I teach in React forms best practices article.

Codepen Demo | Reusable Input Component


I hope you learned something new in this article that helps you in your React journey. Want to learn more about React? Do subscribe to the newsletter for more insightful content.