Form Validation for Frontend Developer

dilarauluturhan
9 min readOct 29, 2023

As frontend developers, we measure the success of web applications not only by eye-catching design but also by functionality that prioritizes users’ experiences. The seamless and accurate experience users experience when filling out a web form plays a critical role in the success of a web application. This is where Form Validation comes into play. Then let’s take a journey into the magical world of form validation.🛸

Topics of this article:

  1. What is Form Validation?
  2. How Does Form Validation Work?
  3. Form Validation with Frontend Technologies
  • Form Validation with JavaScript
  • Form Validation with Reactjs
  • Form Validation with Nextjs

What is Form Validation?✨

Form validation is a powerful tool that web developers use to validate and process user data. It prevents incorrect data entry and ensures that users perform transactions with the correct information. This positively affects the user experience. It not only receives accurate data from users but also plays a critical role in terms of security. It prevents malicious users from sending damaging data to the application.

How Does Form Validation Work?✨

1. Obligation Check:

Some fields in the form may be required (e.g. email address or password). It does not allow the form to be sent without entering data into these fields.

<input type="text" required>

2. Data Type Check:

The accuracy of certain types of data, such as email addresses and phone numbers, can be checked.

<input type="email" required>

3. Length Control:

Form validation can be used to accept or reject data of a certain length.

<input type="text" maxlength="10">

4. Regex Control:

Regular expressions (regex) can be used to accept data that fits a specific pattern.

<input type="text" pattern="[A-Za-z]{3}">

Form Validation with Frontend Technologies✨

Web development is not just about writing codes but also about ensuring users have an interactive and secure experience. This experience depends on enabling users to navigate web applications comfortably and error-free. At this point, we need to know how to use Form Validation.

I would like to show you how you can do Form Validation by giving examples from the Form Validation projects I made with JavaScript, Reactjs and Nextjs.

Form Validation with JavaScript✨

https://form-validation-with-netlify.netlify.app

I would like to talk about how I did validation with JavaScript in this project. If I had to briefly talk about the project, I used Bootstrap and JavaScript in it. I added the codes you see below from the HTML file of the project, and I will tell you what I did step by step:

<div class="container">
<div class="text-bg-dark mt-3 mb-3 p-3 fs-5" id="error"></div>
<div class="row">
<div class="col-sm">
<form id="form" action="/" method="GET">
<div>
<label class="fs-4 fw-semibold" for="name">Name</label>
<input class="form-control" id="name" name="name" type="text" />
</div>
<div>
<label class="fs-4 fw-semibold" for="password">Password</label>
<input class="form-control" id="password" name="password" type="text" />
</div>
<button class="btn btn-dark mt-3 fs-5" type="submit">Submit</button>
</form>
</div>
</div>
const name = document.getElementById('name');
const password = document.getElementById('password');
const form = document.getElementById('form');
const error = document.getElementById('error');

I accessed my elements with name, password, form and error id in the HTML file with document.getElementById because I need to handle the input, button and error message.

form.addEventListener('submit', (e) => {
let messages = [];
if (name.value === "" || name.value == null) {
messages.push("Name is required")
}

if (password.value.length <= 6) {
messages.push("Password must be longer than 6 characters")
}

if (messages.length > 0) {
e.preventDefault();
error.innerText = messages.join(', ')
}
});

Then I used the submit event to listen for the form’s submission event. When the form is submitted, the code inside will run. I created an empty array called messages. Then I checked whether the name field was empty and the password field was less than 6 characters. In case of any error, I added the error messages from the relevant field to the message array.

If the messages array is not empty, I prevented the form from being sent with e.preventDefault() and showed the error message by using the join function in the error element and separating it with a comma.

If you want to try the deployed version, you can access it from this link: https://form-validation-with-netlify.netlify.app

If you would like to examine the project, you can access it from this link: https://github.com/dilarauluturhan/form-validation-with-js

Form Validation with Reactjs✨

https://form-validation-with-reactjs.vercel.app

In this project, I want to explain how you can do pure Form Validation with Reactjs without using a form library. If I had to briefly talk about the project, I would use Tailwind CSS and Reactjs. First, we need to process what we write in the input fields. Then, we need to add warning messages with the help of Regex(Regular Expressions). Let’s examine the code in detail:

After setting up the project, I created a component called FormInput.jsx. I would like to tell you step by step the operations I performed on App.js and FormInput.jsx.

App.js:

function App() {
const [values, setValues] = useState({
username: "",
email: "",
birthday: "",
password: "",
confirm: "",
});

const inputs = [
{
id: 1,
name: "username",
type: "text",
placeholder: "Username",
errorMessage: "Username should be 3-16 characters!",
label: "Username",
pattern: "^[A-Za-z0-9]{3,16}$",
required: true,
},
{
id: 2,
name: "email",
type: "email",
placeholder: "Email",
errorMessage: "It should be a valid email address!",
label: "Email",
pattern: "^[w-.]+@([w-]+.)+[w-]{2,4}$",
required: true,
},
{
id: 3,
name: "birthday",
type: "date",
placeholder: "Birthday",
label: "Birthday",
required: true,
},
{
id: 4,
name: "password",
type: "password",
placeholder: "Password",
errorMessage: "Password should be 8-20 characters!",
label: "Password",
pattern: `^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{8,20}$`,
required: true,
},
{
id: 5,
name: "confirm",
type: "password",
placeholder: "Confirm Password",
errorMessage: "Passwords don't match!",
label: "Confirm Password",
pattern: values.password,
required: true,
},
];

const handleSubmit = (e) => {
e.preventDefault();
};

const onChange = (e) => {
setValues({ ...values, [e.target.name]: e.target.value });
};

console.log(values);
return (
<div className="customBackground bg-local bg-no-repeat bg-cover">
<div className="container mx-auto flex items-center justify-center h-screen">
<form className="bg-formBg p-7 rounded-lg" onSubmit={handleSubmit}>
<h1 className="text-center text-3xl font-semibold">Register</h1>
{inputs.map((input) => (
<FormInput
key={input.id}
{...input}
value={values[input.name]}
onChange={onChange}
/>
))}
<button className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-md px-5 py-2.5 mr-2 mb-2 mt-2 ml-36">
Submit
</button>
</form>
</div>
</div>
);
}

Using the useState hook, I created the values state, which contains the values of the input fields in the form, and the setValues function that will be used to update this state. I could have defined the states one by one, but I learned that it was done as I coded above as a best practice in cases where there are too many input fields.

I assigned the properties of the input fields to be included in the form as an array to the inputs variable. I prevented the form from being submitted with the handleSubmit function. The onChange function tracks the changes in the input fields and updates the values state.

I called the FormInput component for each property in the inputs array. This component takes the label and value of the relevant input field and the onChange function that will be triggered on changes. I also submitted the form with the Submit button.

FormInput.jsx:

const FormInput = (props) => {
const [focused, setFocused] = useState(false);
const { label, errorMessage, onChange, id, ...inputProps } = props;

const handleFocus = (e) => {
setFocused(true);
};

return (
<div className="flex items-center justify-center">
<div>
<label className="block mb-2 text-md font-medium text-gray-900">
{label}
</label>
<input
{...inputProps}
onChange={onChange}
className="bg-gray-50 border border-gray-300 text-gray-900 text-md mb-2 rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-96 p-2.5"
onBlur={handleFocus}
onFocus={() => inputProps.name === "confirm" && setFocused(true)}
focused={focused.toString()}
/>
<span className="text-sm text-red-700 font-semibold hidden">
{errorMessage}
</span>
</div>
</div>
);
};

I created a function called handleFocus. This function sets the focused state to true when the input field is focused (onFocus event).

The <input/> element has access to all properties that come with inputProps and listens for the onChange event. I controlled the focus (onFocus) and defocus (onBlur) events with the handleFocus function. If the name property of the inputProps object is “confirm”, the focused state becomes true when the input field is focused. In case of error, the error message with the errorMessage props is displayed. As you can see, it is so easy to code pure Form Validation without using a form library.

If you want to try the deployed version, you can access it from this link: https://form-validation-with-reactjs.vercel.app

If you would like to examine the project, you can access it from this link: https://github.com/dilarauluturhan/form-validation-with-reactjs

Form Validation with Nextjs✨

And finally, let’s look at the Form Validation project I made with Nextjs. In this project, I have Login and Sign up pages. I performed the verification using the React Hook Form library. I would like to tell you about the validation process through the Login page. Let’s take a look at the code details:

import { useForm } from "react-hook-form";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "../ui/form";
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import Link from "next/link";
import { signIn } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";

const FormSchema = z.object({
email: z.string().min(1, "Email is required!").email("Invalid email!"),
password: z
.string()
.min(1, "Password is required!")
.min(8, "Password must have than 8 characters!"),
});

const LoginForm = () => {
const router = useRouter();
const { toast } = useToast();
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
email: "",
password: "",
},
});

const onSubmit = async (values: z.infer<typeof FormSchema>) => {
const loginData = await signIn("credentials", {
email: values.email,
password: values.password,
redirect: false,
});

if (loginData?.error) {
toast({
title: "Error",
description: "Oops! Something went wrong!",
});
} else {
router.refresh();
router.push("/admin");
}
};

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="w-full space-y-6">
<div className="space-y-2">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Your email</FormLabel>
<FormControl>
<Input placeholder="mail@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Enter your password"
type="password"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<Button className="w-full mt-6" type="submit">
Login
</Button>
</form>
<p className="text-center text-md text-gray-600 mt-5">
If you don&apos;t have an account, please.
<Link className="text-blue-500 hover:underline ml-1" href="/signup">
Sign up
</Link>
</p>
</Form>
);
};

I used Shadcn UI for the UI part and used Shadcn UI’s documentation to use React Hook Form. If you want to review the document, you can look at this link: https://ui.shadcn.com/docs/components/form

Shadcn UI recommends us to use react-hook-form and zod library together. While we perform validation operations with zod, we also create a form with react-hook-form.

If we look at the codes, I defined a schema (FormSchema) for form validation with the zod library. I defined the properties and schema required for form management with the useForm hook. I created an asynchronous function called onSubmit. This function defines the operations to be performed during form submission.

The form component manages submission operations with form.handleSubmit. Form fields are created with FormField, FormItem, FormLabel, FormControl and FormMessage components. As you can see in the Login component, I easily performed the validation using zod and react-hook-form.

If you want to try the deployed version, you can access it from this link: https://github.com/dilarauluturhan/form/tree/master/src/components/form

I tried to explain Form Validation to you in practice with three different projects. We have reviewed that Form Validation is an essential part of modern web development and significantly impacts the user experience. An accurate and effective Form Validation not only ensures that users enter the correct data but also increases the security of web applications. You can provide an effective experience to your users by applying the techniques you learned in this article to your future projects.

If you have questions, comments, or topic suggestions, please let me know. Your feedback helps me further improve my blog series. See you in the next article!

Click if you want to contact me!👩🏻‍💻

You can support my articles by buying a coffee.☕️

--

--