A modern way of building forms in React.

A modern way of building forms in React.

ยท

6 min read

We all know how important forms are in web applications and even in mobile applications. Forms are one of the best tools for data collection for our application. We use forms on daily basis from authentication to making entries of data in our applications. So, it is also very important to build forms that can help us to remove noisy data.

Form validation is one of the important yet annoying features to implement if, we decide to build it from ground zero. So, Let's see how we can implement form validations in a modern way with more accuracy and fewer coding efforts.

What are we going to build?

For this tutorial, we are going to implement a registration form in react using React Hooks Form and Zod library for schema validations.

Get to know the libraries first.

  1. React Hook Form:

    React Hook Form is a lightweight library that uses react hooks to manage the form field data. It uses uncontrolled components that allow us to avoid unnecessary re-renders

  2. Zod:

    Zod is a schema validation library. Since it is based on type script it supports the strong type. It is a very lightweight library used for schema validation that does not have any other dependencies.

Implementation

First of all, let's create a new React + TypeScript project. I'm going to use vite for project scaffolding but you can use tools of your own choice.

npm create vite@latest form-validation -- --template react-ts

after running the above command in your terminal follow the on-screen instructions.

After that let's install our libraries

npm i bootstrap react-hook-form zod @hookform/resolvers

We are using bootstrap for the minimalistic design of our form and then we are using react hook form and Zod libraries for validation.

Now, let's create a new folder in our src folder called components. Inside this folder create a new file called Registration. tsx. Your folder structure should look like this.

Now let's remove the template code from our App.tsx and import our Registration component inside it.

Replace your App.tsx code with the following code

import Registration from "./components/Registration"

function App() {
  return (
    <div>
      <Registration />
    </div>
  )
}

export default App

Also, delete your app.css and index.css files because we are going to use bootstrap for styling. So, let's remove the CSS reference from the main.tsx file and import bootstrap in this file.

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import 'bootstrap/dist/css/bootstrap.css'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

Let's design our form using bootstrap. So, replace your Registration.tsx code with the following code

const Registration = () => {
  return (
    <div className='vh-100 d-flex justify-content-center align-items-center container'>
        <form className='w-50'>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Name</label>
                <input type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
            </div>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Email</label>
                <input type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
            </div>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Age</label>
                <input type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
            </div>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Password</label>
                <input type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
            </div>
            <button type="submit" className="btn btn-primary">Submit</button>
        </form>
    </div>
  )
}

export default Registration

As we already know that we need to track the state of our form fields to make. We will pass on those duties to our react-hook-form library.

Let's start by importing the useForm hook from react-hook-form library. So, inside your Registration.tsx file do the following

import { useForm } from 'react-hook-library'

Once we have imported the required hook. We need to use this hook and destructure some required methods. So, we will do it as following

const {register, handleSubmit, reset, formState: {errors, isValid}} = useForm();

we are destructing register (this will register the input component of the given name), handleSubmit (this method will be used during submission as it will have access to all the data that the user will enter inside the form fields). reset (used to empty the filled data). formState has two useful properties errors and isValid. errors can help give the user feedback on what went wrong? and isValid can be used to make our submission button enable and disable.

So our form will look like the following code now.

import {useForm} from 'react-hook-form';

const Registration = () => {
  const {register, handleSubmit, reset, formState: {errors, isValid}} = useForm();
  return (
    <div className='vh-100 d-flex justify-content-center align-items-center container'>
        <form className='w-50'>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Name</label>
                <input  {...register('name')} type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
            </div>
            <div className="my-3">
                <label htmlFor="email" className="form-label">Email</label>
                <input {...register('email')} type="email" id='email' className="form-control" placeholder='Enter your email address'/>
            </div>
            <div className="my-3">
                <label htmlFor="age" className="form-label">Age</label>
                <input  {...register('age', {valueAsNumber: true})} type="number" id='age' className="form-control" placeholder='Enter your age'/>
            </div>
            <div className="my-3">
                <label htmlFor="password" className="form-label">Password</label>
                <input {...register('password')} type="password" id='password' className="form-control" placeholder='Enter your Password'/>
            </div>
            <button type="submit" className="btn btn-primary">Submit</button>
        </form>
    </div>
  )
}

export default Registration

Since we have sorted out the form field state. It is a good time for us to provide schema validation to our form. That is where Zod library comes to our rescue.

So let's define our schema

const schema = z.object({
    name: z.string().min(3, {message: 'Name should be atleast 3 characters long.'}),
    email: z.string().email({message: 'Email field is required. Please Provide valid email'}),
    age: z.number({invalid_type_error: 'Age is required'}).min(18, {message: 'You need to be atleast 18 years old to register with us.'}),
    password: z.string().min(5, {message: 'Password needs to be atleast 8 characters long'})
})

PS: - You can tweak these validation conditions according to your own needs. You can also read more about them on Zod.dev.

After that we need to hook this with our useForm hook and provide a resolver.

Our final Code of registration.tsx will look like this

import {useForm} from 'react-hook-form';
import {z} from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';


const schema = z.object({
    name: z.string().min(3, {message: 'Name should be atleast 3 characters long.'}),
    email: z.string().email({message: 'Email field is required. Please Provide valid email'}),
    age: z.number({invalid_type_error: 'Age is required'}).min(18, {message: 'You need to be atleast 18 years old to register with us.'}),
    password: z.string().min(5, {message: 'Password needs to be atleast 8 characters long'})
})

type FormData = z.infer<typeof schema>

const Registration = () => {
  const {register, handleSubmit, reset, formState: {errors, isValid}} = useForm<FormData>({resolver: zodResolver(schema)});
  return (
    <div className='vh-100 d-flex justify-content-center align-items-center container'>
        <form className='w-50' onSubmit={handleSubmit(data => console.log(data))}>
            <div className="my-3">
                <label htmlFor="name" className="form-label">Name</label>
                <input  {...register('name')} type="text" id='name' className="form-control" placeholder='Enter your full Name'/>
                {errors.name && <p className="text-danger">{errors.name.message}</p> }
            </div>
            <div className="my-3">
                <label htmlFor="email" className="form-label">Email</label>
                <input {...register('email')} type="email" id='email' className="form-control" placeholder='Enter your email address'/>
                {errors.email && <p className="text-danger">{errors.email.message}</p> }
            </div>
            <div className="my-3">
                <label htmlFor="age" className="form-label">Age</label>
                <input  {...register('age', {valueAsNumber: true})} type="number" id='age' className="form-control" placeholder='Enter your age'/>
                {errors.age && <p className="text-danger">{errors.age.message}</p> }
            </div>
            <div className="my-3">
                <label htmlFor="password" className="form-label">Password</label>
                <input {...register('password')} type="password" id='password' className="form-control" placeholder='Enter your Password'/>
                {errors.password && <p className="text-danger">{errors.password.message}</p> }
            </div>
            <button type="submit" className="btn btn-primary">Submit</button>
        </form>
    </div>
  )
}

export default Registration

Output

Thank you for reading this tutorial. I hope this tutorial will help you in implementing the form Validation in react in a smooth manner.

I hope I was able to deliver something good to you guys โ˜บ. Feedback, suggestions, etc are always welcomed.

Have a fun and safe time and Thank you so much for dedicating your time to go through this blog โค.

Let's Learn and Grow Together. Adios amigos/amigas hasta la proxima vez ๐Ÿ’œโ˜•