paint-brush
The Ultimate Guide to Effectively Using and Validating Forms in Reactby@theankurtyagi
807 reads
807 reads

The Ultimate Guide to Effectively Using and Validating Forms in React

by Ankur TyagiJanuary 5th, 2024
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In this post, I delve into React forms, discussing controlled vs. uncontrolled forms, providing examples, and exploring various validation techniques. I cover everything from basic HTML attributes to advanced validation with Yup and Zod, managing inputs with React Hook Form, and using Formik for form management. I conclude with best practices and insights for effectively handling forms in React.

Company Mentioned

Mention Thumbnail
featured image - The Ultimate Guide to Effectively Using and Validating Forms in React
Ankur Tyagi HackerNoon profile picture

A form is an integral component of every website. Whether you’re registering for a service, signing into your email, or sending messages to your friends, you’ve always needed a form for these actions. Without forms, websites and web applications may have been read-only, offering only a one-way method of communication where users cannot send messages or data to the system.


In this tutorial, you’ll learn how to create and validate forms in React, ensuring that users provide data in a specified format.


Additionally, you’ll learn how to use validation schema libraries, such as Yup and Zod, and other form management libraries, like React Hook Form and Formik.

Controlled vs Uncontrolled forms in React

A controlled form is one where React states manage and handle the form values – allowing you to perform various actions or modify the input as the user enters it.

On the other hand, in an uncontrolled form, the form components manage their values. The data are read-only until the user submits the form, making it more suitable for creating simple forms.

Let’s create a Contact form that accepts a name, email, and message using both methods.

Controlled Form Example

import { useState } from "react";

const App = () => {
    //👇🏻 states for modifying the form inputs
    const [name, setName] = useState("");
    const [email, setEmail] = useState("");
    const [message, setMessage] = useState("");

    //👇🏻 executes when a user submits the form
    const handleSubmit = (event) => {
        event.preventDefault();
        console.log({ name, email, message });        setName("");        setEmail("");        setMessage("");


    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Contact Us</h2>
                <label htmlFor='name'>Name</label>
                <input
                    type='text'
                    id='name'
                    value={name}
                    onChange={(e) => setName(e.target.value)}
                />
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                />
                <label htmlFor='message'>Message</label>
                <textarea
                    rows={5}
                    id='message'
                    value={message}
                    onChange={(e) => setMessage(e.target.value)}
                />
                <button type='submit'>SEND</button>
            </form>
        </div>
    );
};


From the code snippet above, the React states manage and store the form values, enabling us to control the state values using the `setState` function and perform various actions as the user provides the input.

Uncontrolled Form Example

const App = () => {
    //👇🏻 executes when a user submits the form
    const handleSubmit = (event) => {
        event.preventDefault();
        const formData = new FormData(event.target);
        const object = Object.fromEntries(formData);
        console.log(object);
    };
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Contact Us</h2>
                <label htmlFor='name'>Name</label>
                <input type='text' id='name' name='name' />
                <label htmlFor='email'>Email</label>
                <input type='email' id='email' name='email' />
                <label htmlFor='message'>Message</label>
                <textarea rows={5} id='message' name='message' />
                <button type='submit'>SEND</button>
            </form>
        </div>
    );
};


The code snippet above uses the JavaScript FormData object to collect the form inputs. Unlike the previous example, you cannot modify the user’s input directly because the form elements manage the inputs. This method is called an Uncontrolled Form.


An uncontrolled form can also be created using the React useRef hook.


It assigns a reference to each input field to enable us to access their values.


import { useRef } from "react";

const App = () => {
    const nameRef = useRef(null);
    const emailRef = useRef(null);
    const messageRef = useRef(null);

    const handleSubmit = (event) => {
        event.preventDefault();
        // Access input values directly using refs
        const name = nameRef.current.value;
        const email = emailRef.current.value;
        const message = messageRef.current.value;
        console.log({ name, email, message });
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Contact Us</h2>
                <label htmlFor='name'>Name</label>
                <input type='text' id='name' ref={nameRef} />
                <label htmlFor='email'>Email</label>
                <input type='email' id='email' ref={emailRef} />
                <label htmlFor='message'>Message</label>
                <textarea rows={5} id='message' ref={messageRef} />
                <button type='submit'>SEND</button>
            </form>
        </div>
    );
};

Form Validation in React

Forms play a crucial role in web applications, but they can be a source of harm to your application. Users can enter wrongly formatted data, and attackers could exploit forms to steal sensitive information or crash the application.


To prevent this occurrence, you need to validate form inputs. In modern applications, form inputs are validated both on the client and server side of the application. This process ensures that inputs are of the desired format, preventing harmful inputs or attacks from being processed by the application.


There are various ways of validating form inputs in React. In the upcoming sections, you’ll learn about a few of these methods.

Using the Default HTML Attributes

HTML form elements provide some default attributes that enable us to validate form inputs before saving them within the application.


A few of them are:

  • The required attribute states that an input field must be filled before a user can submit the form.
  • The minlength and maxlength attributes ensure that the form input must be of a desired length.
  • The accept attribute in file upload that states the acceptable file formats.


Consider the code snippet below:

import { useState } from "react";

const App = () => {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");
    const [email, setEmail] = useState("");

    //👇🏻 executes when a user submits the form
    const handleSubmit = (event) => {
        event.preventDefault();
        console.log({ username, password, email });
        alert("Form Submitted ✅");
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    name='username'
                    value={username}
                    onChange={(e) => setUsername(e.target.value)}
                    required
                    minLength={6}
                />
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    name='email'
                    value={email}
                    required
                    onChange={(e) => setEmail(e.target.value)}
                />
                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    name='password'
                    value={password}
                    required
                    minLength={8}
                    onChange={(e) => setPassword(e.target.value)}
                />
                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


The code snippet above ensures the user provides a username, email, and password before submitting the form.


The form accepts a username with a minimum of six characters, an email, and a password with a minimum of eight characters.


React Forms

Using Custom JavaScript Functions

Another method of validating form inputs is by creating custom JavaScript functions that check if the input is of the desired format before submitting the form.


In this case, you’ll have to check for various errors and display error messages for each case.


Consider the code snippet below:

import { useState } from "react";

const App = () => {
    //👇🏻 state for each input
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");
    const [email, setEmail] = useState("");

    //👇🏻 state for error messages
    const [errorMessage, setErrorMessage] = useState({
        username: "",
        email: "",
        password: "",
    });

    // 👇🏻 Function to update error messages
    const updateErrorState = (fieldName, message) => {
        setErrorMessage((prevState) => ({
            ...prevState,
            [fieldName]: message,
        }));
    };

    //👇🏻 function for validating inputs
    const validateInput = (data) => {
        const { value, name } = data;
        if (name === "username") {
            if (value.trim() === "") {
                updateErrorState(name, "Username is required");
            } else if (value.length < 6) {
                updateErrorState(name, "Username must be at least 6 characters");
            } else {
                updateErrorState(name, "");
            }
        } else if (name === "email") {
            if (value.trim() === "") {
                updateErrorState(name, "Email is required");
            } else {
                updateErrorState(name, "");
            }
        } else {
            if (value.trim() === "") {
                updateErrorState(name, "Password is required");
            } else if (value.length < 8) {
                updateErrorState(name, "Password must be at least 8 characters");
            } else {
                updateErrorState(name, "");
            }
        }
    };

    //👇🏻 submits the form
    const handleSubmit = (e) => {
        e.preventDefault();
        if (
            Object.values(errorMessage).some((message) => message !== "") ||
            !email ||
            !password ||
            !username
        ) {
            alert("Invalid Credentials ❌");
            return;
        }
        alert("Form Submitted ✅");
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    name='username'
                    value={username}
                    onChange={(e) => {
                        setUsername(e.target.value);
                        validateInput(e.target);
                    }}
                />
                {errorMessage.username && <p>{errorMessage.username}</p>}
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    name='email'
                    value={email}
                    onChange={(e) => {
                        setEmail(e.target.value);
                        validateInput(e.target);
                    }}
                />
                {errorMessage.email && <p>{errorMessage.email}</p>}
                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    name='password'
                    value={password}
                    onChange={(e) => {
                        setPassword(e.target.value);
                        validateInput(e.target);
                    }}
                />
                {errorMessage.password && <p>{errorMessage.password}</p>}
                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


The validInput function ensures the user’s input adheres to the specified constraints before submitting the form. Otherwise, it displays the necessary error message to the user.

The function validates that the username, email, and password are not empty, the username contains at least six characters, and the password has at least eight characters.


Form-Validation-in-React

Form Validation with Yup

Yup is a simple JavaScript schema validator that provides multiple functions to enable us to validate whether an object matches a particular set of rules.


It provides a simple and declarative way to define validation rules and ensures that the form inputs adhere to them.


To validate forms with Yup, you need to install the package by running the code snippet below:


npm install yup


Next, create a schema for the form data. The schema describes the shape, constraints, and data type of the values to be retrieved from the form. Yup validates this schema against the user’s input to ensure that the rules stated within the schema are adhered to.


For example, the schema for a form containing an email, a username with a minimum of six characters, and a password of at least eight characters is shown below:


import { object, string } from "yup";

//👇🏻 user schema with Yup
const userSchema = object().shape({
    username: string()
        .required("Username is required")
        .min(6, "Username must be at least 6 characters"),
    email: string().email("Invalid Email").required("Email is required"),
    password: string()
        .required("Password is required")
        .min(8, "Password must be at least 8 characters"),
});


The userSchema object describes the form inputs.


For instance, the username is required and must be a string with a minimum of six characters. The string arguments within the function are the errors displayed when an input does not match the specific rule.


Let’s examine the complete code for the form:

import { useState } from "react";
import { object, string } from "yup";

const App = () => {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");
    const [email, setEmail] = useState("");
    const [errorMessage, setErrorMessage] = useState({
        username: "",
        email: "",
        password: "",
    });
    //👇🏻 user schema with Yup
    const userSchema = object().shape({
        username: string()
            .required("Username is required")
            .min(6, "Username must be at least 6 characters"),
        email: string().email("Invalid Email").required("Email is required"),
        password: string()
            .required("Password is required")
            .min(8, "Password must be at least 8 characters"),
    });

    //👇🏻 submits the form
    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            await userSchema.validate(
                { username, email, password },
                { abortEarly: false }
            );
            console.log({ email, username, password });
            setErrorMessage({ username: "", email: "", password: "" });
            alert("Form Submitted ✅");
        } catch (error) {
            //👇🏻 update the  errors
            const errors = error.inner.reduce((accumulator, currentValue) => {
                accumulator[currentValue.path] = currentValue.message;
                return accumulator;
            }, {});
            setErrorMessage(errors);
            return;
        }
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    name='username'
                    value={username}
                    onChange={(e) => setUsername(e.target.value)}
                />
                {errorMessage.username && <p>{errorMessage.username}</p>}
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    name='email'
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                />
                {errorMessage.email && <p>{errorMessage.email}</p>}
                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    name='password'
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                />
                {errorMessage.password && <p>{errorMessage.password}</p>}
                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


The code snippet above compares the user’s input with the schema when the user submits the form and displays the error message if the form inputs do not match the rules.


React Form- Validation

Form Validation with Zod

Zod is a TypeScript-first schema declaration and validation library. It enables you to define the schema for your form inputs using concise functions and validates the user inputs against the schema, just like Yup.


You can install Zod by running the code snippet below:


npm install zod


Create a schema for the form inputs.

It describes the form inputs and ensures that they follow the rules.

import { z, ZodError } from "zod";

//👇🏻 user schema with Zod
const schema = z.object({
    username: z.string().min(6, {
        message: "Username must be at least 6 characters",
    }),
    email: z.string().email({ message: "Invalid email address" }),
    password: z.string().min(8, {
        message: "Password must be at least 8 characters",
    }),
});


The code snippet above ensures that all the values are string and not empty, the username is at least six characters, the email is valid, and the password is a minimum of eight characters. Otherwise, it returns the error message within the functions.


Let’s examine the complete code for the form:

import { useState } from "react";
import { z, ZodError } from "zod";

const App = () => {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");
    const [email, setEmail] = useState("");
    const [errorMessage, setErrorMessage] = useState({
        username: "",
        email: "",
        password: "",
    });
    //👇🏻 user schema with Zod
    const schema = z.object({
        username: z.string().min(6, {
            message: "Username must be at least 6 characters",
        }),
        email: z.string().email({ message: "Invalid email address" }),
        password: z.string().min(8, {
            message: "Password must be at least 8 characters",
        }),
    });

    //👇🏻 submits the form
    const handleSubmit = (e) => {
        e.preventDefault();
        try {
            //👇🏻 validates the inputs
            schema.parse({ username, email, password });
            console.log({ email, username, password });
            setErrorMessage({ username: "", email: "", password: "" });
            alert("Form Submitted ✅");
        } catch (error) {
            //👇🏻 updates error message
            if (error instanceof ZodError) {
                const newFormErrors = {};
                error.errors.forEach((err) => {
                    const fieldName = err.path[0];
                    newFormErrors[fieldName] = err.message;
                });
                setErrorMessage(newFormErrors);
            }
        }
    };

    return (
        <div>
            <form onSubmit={handleSubmit}>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    name='username'
                    value={username}
                    onChange={(e) => setUsername(e.target.value)}
                />
                {errorMessage.username && <p>{errorMessage.username}</p>}
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    name='email'
                    value={email}
                    onChange={(e) => setEmail(e.target.value)}
                />
                {errorMessage.email && <p>{errorMessage.email}</p>}
                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    name='password'
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                />
                {errorMessage.password && <p>{errorMessage.password}</p>}
                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


When a user submits the form, Zod validates the form inputs and returns the error messages for invalid fields.


However, you’ll notice that in Yup and Zod, validation occurs when the user submits the form. How can we validate the inputs as the user enters them?


In the upcoming section, you’ll learn how to use some advanced yet simple form management libraries.

Managing Form Inputs with React Hook Form

React Hook Form is a popular library that enables us to build simple, scalable, and highly performant forms. It manages the form inputs, enforces validation, and displays the necessary errors when necessary.


React Hook Form also provides several features, such as a dev tool for monitoring form inputs during development, HTML validation methods, the ability to add custom and regex pattern validation, and support for schema validation libraries, such as Yup or Zod, enabling us to build highly functional and secured forms.


To use React Hook Form, install its package by running the code snippet below.


npm install react-hook-form


Let’s create a signup form using the React Hook Form library:

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

const App = () => {
    const { register, handleSubmit, formState } = useForm();
    const { errors } = formState;

    //👇🏻 submits the form
    const onSubmit = (data) => {
        console.log("Form Submitted", data);
        alert("Form Submitted ✅");
    };

    return (
        <div>
            <form onSubmit={handleSubmit(onSubmit)} noValidate>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    {...register("username", {
                        required: "Username is required",
                        minLength: {
                            value: 6,
                            message: "Username must be at least 6 characters",
                        },
                    })}
                />
                <p>{errors.username?.message}</p>
                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    {...register("email", {
                        required: "Email is required",
                        pattern: {
                            value: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
                            message: "Invalid email address",
                        },
                    })}
                />
                <p>{errors.email?.message}</p>
                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    {...register("password", {
                        required: "Password is required",
                        minLength: {
                            value: 8,
                            message: "Password must be at least 8 characters",
                        },
                    })}
                />
                <p>{errors.password?.message}</p>
                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


The code snippet uses React Hook Form features such as form data management, form validation, form submission, and error display.


  • The useForm hook contains the functions required for managing the form.
  • The handleSubmit function handles the form submission, and the `novalidate` attribute on the form element deactivates HTML validation to give React Hook Form complete control of the form.
  • The register function registers each input field to React Hook Form, allowing it to manage the form state. The register function accepts two arguments – the field name and the validation object.
  • The formState object contains the error messages associated with each input field.


Controlled vs Uncontrolled-forms-in-React


React Hook Form gives you complete control of your form and allows you to add various schema validation libraries to build highly secured and complex forms.


In the upcoming sections, you’ll learn how to add Yup and Zod to React Hook Form.

Integrating Yup with React Hook Form

You can extend React Hook Form using its Resolvers package. It enables you to add any validation library to the React Hook Form. Therefore, install Yup and the Resolvers package by running the code snippet below.


npm install @hookform/resolvers yup


Create the form schema using Yup as shown below:

import * as yup from "yup";

const schema = yup
    .object()
    .shape({
        username: yup
            .string()
            .required("Username is required")
            .min(6, "Username must be at least 6 characters"),
        email: yup.string().required("Email is required"),
        password: yup
            .string()
            .required("Password is required")
            .min(8, "Password must be at least 8 characters"),
    })
    .required();


Finally, add the schema to the useForm hook using the Yup resolver. It validates the form inputs and ensures that it matches the schema.

import { yupResolver } from "@hookform/resolvers/yup";

const App = () => {
    const { register, handleSubmit, formState } = useForm({
        resolver: yupResolver(schema),
    });
    const { errors } = formState;

    //👇🏻 submits the form
    const onSubmit = (data) => {
        console.log("Form Submitted", data);
        alert("Form Submitted ✅");
    };

    return (
        <div>
            <form onSubmit={handleSubmit(onSubmit)} noValidate>
                <h2>Log in</h2>

                <label htmlFor='name'>Username</label>
                <input type='text' id='username' {...register("username")} />
                <p>{errors.username?.message}</p>

                <label htmlFor='email'>Email</label>
                <input type='email' id='email' {...register("email")} />
                <p>{errors.email?.message}</p>

                <label htmlFor='password'>Password</label>
                <input type='password' id='password' {...register("password")} />
                <p className='text-red-500 mb-4 '>{errors.password?.message}</p>

                <button type='submit'>REGISTER</button>
            </form>
        </div>
    );
};


React-Forms-Validation

Integrating Zod with React Hook Form

You can add Zod to the React Hook Form using the Resolvers package. Install Zod and the Resolvers package by running the code snippet below.


npm install @hookform/resolvers zod


Create the form schema using Zod as shown below:


import * as z from "zod";

const schema = z.object({
    username: z.string().min(6, {
        message: "Username must be at least 6 characters",
    }),
    email: z.string().email({ message: "Invalid email address" }),
    password: z.string().min(8, {
        message: "Password must be at least 8 characters",
    }),
});


Finally, add the schema to the useForm hook using the Zod resolver. Zod provides the required schema for the form, and React Hook Form ensures that the provided inputs conform to the schema before submitting the form.


Congratulations! You’ve learnt how to create flexible and secure forms using React Hook Form, and you’ve also learnt how to extend it to accommodate various schema validation libraries, like Yup and Zod.

Managing Form Inputs with Formik

Formik is a simple library that manages form data, handles form submission and validation, and provides feedback with error messages. It provides a simple way of working with forms using utilities and in-built components.


Like React Hook Form, Formik is a flexible library that allows you to extend its features using schema validation libraries like Yup and Zod.


Run the code snippet below to install Formik

npm install formik


Like the useForm hook in React Hook Form, Formik provides a hook called – the useFormik hook. It enforces data validation, handles form submission, and manages the user’s input.


The useFormik hook accepts an object containing the initial values of the form fields, the onSubmit function, and the validation function.


import { useFormik } from "formik";

const formik = useFormik({
    initialValues: {
        //👇🏻 form default input
        username: "",
        email: "",
        password: "",
    },
    //👇🏻 handles form submission
    onSubmit: (values) => {
        console.log(values);
    },
    //👇🏻 handles form validation
    validate: (values) => {
        //👉🏻 validate form inputs
    },
});


Let’s re-create the signup form:


import { useFormik } from "formik";

//👇🏻 form validation function
const validate = (values) => {
    // validate function
    let errors = {};
    if (!values.username) {
        errors.username = "Required";
    } else if (values.username.length < 6) {
        errors.username = "Must be 6 characters or more";
    }

    if (!values.email) {
        errors.email = "Required";
    } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
        errors.email = "Invalid email address";
    }

    if (!values.password) {
        errors.password = "Required";
    } else if (values.password.length < 8) {
        errors.password = "Must be 8 characters or more";
    } else if (values.password === "password") {
        errors.password = "Must not be password";
    }
    return errors;
};

//👇🏻 form submission function
const onSubmit = (values) => {
    console.log(values);
    alert("Form submitted successfully ✅");
};

const App = () => {
    const formik = useFormik({
        initialValues: {
            username: "",
            email: "",
            password: "",
        },
        onSubmit,
        validate,
    });

    return (
        <div>
            <form onSubmit={formik.handleSubmit}>
                <h2>Log in</h2>
                <label htmlFor='name'>Username</label>
                <input
                    type='text'
                    id='username'
                    name='username'
                    {...formik.getFieldProps("username")}
                />
                {formik.touched.username ? <p>{formik.errors.username}</p> : null}

                <label htmlFor='email'>Email</label>
                <input
                    type='email'
                    id='email'
                    name='email'
                    {...formik.getFieldProps("email")}
                />
                {formik.touched.email ? (
                    <p className='text-red-500 mb-4 '>{formik.errors.email}</p>
                ) : null}

                <label htmlFor='password'>Password</label>
                <input
                    type='password'
                    id='password'
                    name='password'
                    {...formik.getFieldProps("password")}
                />
                {formik.touched.password ? <p>{formik.errors.password}</p> : null}

                <button type='submit'>REGISTER</button>
            </form>
        </div>
    )};


From the code snippet above:

  • The initialValue object contains the name and default value of the input fields within the form.
  • The formik.getFieldProps() function manages the field values.
  • The formik.handleSubmit executes the onSubmit function registered within the useFormik hook.
  • The validate function creates an error object containing the constraints or validation needed within the form. It displays the error messages when the input does match the validation rules.

Integrating Formik with Validation Schemas

Formik allows you to use various validation libraries to help build robust forms with complex validation rules. This enables us to leverage the strengths of both libraries to create scalable and secured forms.


For instance, you can create a validation schema using Yup and add it to the useFormik hook, as shown below:

import { object, string } from "yup";

const App = () => {
    //👇🏻 Yup validation schema
    const validationSchema = object().shape({
        username: string()
            .required("Username is required")
            .min(6, "Username must be at least 6 characters"),
        email: string().email("Invalid email").required("Email is required"),
        password: string()
            .required("Password is required")
            .min(8, "Password must be at least 8 characters"),
    });

    //👇🏻 useFormik hook using the Yup validation schema
    const formik = useFormik({
        initialValues: {
            username: "",
            email: "",
            password: "",
        },
        onSubmit: onSubmit,
        validationSchema: validationSchema,
    });
};


From the code snippet above, Formik provides a validationSchema property that enables us to use a schema validation library.


It ensures that the values match the defined schema.

Using Formik components

Formik provides an easier way of creating forms with the use of components. Formik components enable you to create forms in a cleaner way with less code. They simplify the process of creating dynamic and user-friendly form interfaces within your React applications.


Let’s recreate the signup form using Formik components:


import { Form, Formik, Field, ErrorMessage } from "formik";const App = () => {
    const initialValues = {
        username: "",
        email: "",
        password: "",
    };

    return (
        <div>
            <Formik
                initialValues={initialValues}
                onSubmit={onSubmit}
                validationSchema={validationSchema}
            >
                <Form>
                    <h2>Log in</h2>
                    <label htmlFor='name'>Username</label>
                    <Field type='text' id='username' name='username' />
                    <ErrorMessage name='username' render={(msg) => <p>{msg}</p>} />

                    <label htmlFor='email'>Email</label>
                    <Field type='email' id='email' name='email' />
                    <ErrorMessage name='email' render={(msg) => <p>{msg}</p>} />

                    <label htmlFor='password'>Password</label>
                    <Field type='password' id='password' name='password' />
                    <ErrorMessage name='password' render={(msg) => <p>{msg}</p>} />

                    <button type='submit'>REGISTER</button>
                </Form>
            </Formik>
        </div>
    );
};


The Formik component wraps the entire form and accepts the initial values, the `onSubmit` function, and the validation method as props.


Formik also provides other components, such as Form, Field, and ErrorMessage used to replace the form tag, input tag, and error message respectively.

Conclusion

So far, you have learned how to work with forms in React.


We discussed the validation of form inputs using custom JavaScript functions, as well as using Zod and Yup. Additionally, you learned how to use form management libraries, such as React Hook Form and Formik.


Form management libraries, like React Hook and Formik, are suitable for handling form values that require complex validation. Zod and Yup can be used to validate the data received from the form before saving the data to the database.


These tools enable us to build scalable, flexible, and secured forms in React.


Thank you for reading. If you like this blog and want to read more about ReactJS and JavaScript, start reading some recent articles.


Get career, business, writing, and life advice for engineers, tech leads, and engineering managers right to your inbox. Join me at bytesizedbets on growing your career and making smart moves in tech. Simple, direct, and made for you. Subscribe today!

ByteSizedBets


Also published here.