Form validation is a critical aspect of web development, ensuring that user inputs are accurate and meet the required criteria before submission. Effective form validation enhances user experience by providing immediate feedback and preventing errors. In this blog post, we’ll explore how to implement form validation in a React application using Fluent UI components and the Zod validation library. I will use TypeScript for all examples to ensure type safety and robust code. Our example will involve a user registration form with fields for username and email.
Thank me by sharing on Twitter 🙏
Setting Up Fluent UI and Zod
To begin, ensure you have @fluentui/react
and zod
installed in your project. Fluent UI provides a comprehensive set of components that follow Microsoft’s design language, while Zod is a TypeScript-first schema declaration and validation library.
npm install @fluentui/react zod
In our project, i’ll create a form component called UserPage
that will handle user input and validation. This form will include fields for username and email, and it will display appropriate error messages if the inputs do not meet the validation criteria defined by Zod.
Defining the Zod Schema
The first step in our validation process is defining the schema using Zod. The schema will specify the rules for each form field.
import { z } from 'zod';
// Define Zod schema
const schema = z.object({
username: z.string()
.nonempty({ message: 'Username is required' }),
email: z.string()
.nonempty({ message: 'Email is required' })
.email({ message: 'Invalid email address' }),
});
The schema ensures that the username is a non-empty string and that the email is both non-empty and follows the correct email format.
Elon Musk
$20.36 (as of December 21, 2024 19:39 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Nexus: A Brief History of Information Networks from the Stone Age to AI
$21.66 (as of December 21, 2024 19:39 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Logitech M510 Wireless Computer Mouse for PC with USB Unifying Receiver - Graphite
$19.99 (as of December 21, 2024 08:38 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Creating the UserPage Component
Next, create the UserPage
component. This component will manage form state, handle user input, and validate the inputs against the Zod schema.
import React, { useState } from 'react';
import { Input, Field, Button } from '@fluentui/react';
import { z } from 'zod';
// Define Zod schema
const schema = z.object({
username: z.string().nonempty({ message: 'Username is required' }),
email: z
.string()
.nonempty({ message: 'Email is required' })
.email({ message: 'Invalid email address' }),
});
const UserPage = () => {
const [formValues, setFormValues] = useState({ username: '', email: '' });
const [formErrors, setFormErrors] = useState({ username: '', email: '' });
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormValues({
...formValues,
[name]: value,
});
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const result = schema.safeParse(formValues);
if (!result.success) {
const errors = result.error.flatten().fieldErrors;
setFormErrors({
username: errors.username ? errors.username[0] : '',
email: errors.email ? errors.email[0] : '',
});
} else {
setFormErrors({ username: '', email: '' });
console.log('Form Submitted', formValues);
}
};
return (
<form onSubmit={handleSubmit}>
<Field label="Username" validationMessage={formErrors.username}>
<Input
name="username"
value={formValues.username}
onChange={handleChange}
/>
</Field>
<Field label="Email" validationMessage={formErrors.email}>
<Input
name="email"
value={formValues.email}
onChange={handleChange}
/>
</Field>
<Button appearance="primary">
Submit
</Button>
</form>
);
};
export default UserPage;
Handling Form State
The UserPage
component maintains form state using the useState
hook. It has two state variables: formValues
for storing the current input values and formErrors
for storing any validation errors.
const [formValues, setFormValues] = useState({ username: '', email: '' });
const [formErrors, setFormErrors] = useState({ username: '', email: '' });
The handleChange function updates the form values state whenever the user types into an input field.
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormValues({
...formValues,
[name]: value,
});
};
Validating Inputs
When the form is submitted, the handleSubmit
function validates the inputs against the Zod schema using safeParse
.
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const result = schema.safeParse(formValues);
if (!result.success) {
const errors = result.error.flatten().fieldErrors;
setFormErrors({
username: errors.username ? errors.username[0] : '',
email: errors.email ? errors.email[0] : '',
});
} else {
setFormErrors({ username: '', email: '' });
console.log('Form Submitted', formValues);
}
};
If validation fails, safeParse
returns an error object which is then used to set the formErrors
state. If validation is successful, the form errors are cleared, and the form values are logged to the console.
Rendering the Form
The form fields are rendered using Fluent UI’s Input
and Field
components. The validationMessage
prop of the Field
component is used to display validation errors.
return (
<form onSubmit={handleSubmit}>
<Field label="Username" validationMessage={formErrors.username}>
<Input
name="username"
value={formValues.username}
onChange={handleChange}
/>
</Field>
<Field label="Email" validationMessage={formErrors.email}>
<Input
name="email"
value={formValues.email}
onChange={handleChange}
/>
</Field>
<Button appearance="primary">
Submit
</Button>
</form>
);
Conclusion
Implementing form validation in a React application using Fluent UI and Zod is a straightforward process that ensures robust and type-safe forms. By defining a schema with Zod and managing form state manually, you can provide immediate feedback to users and prevent errors before form submission. This approach not only improves user experience but also helps maintain the integrity of the data being collected.
In this blog post, we’ve walked through the setup and implementation of a simple user registration form with validation. By leveraging the power of TypeScript, Fluent UI, and Zod, you can create highly maintainable and user-friendly forms in your React applications.