How to Validate a Date Range Picker with Zod in TypeScript Projects

RMAG news

What is Zod?

Zod is a TypeScript-first schema declaration and validation library.

Prerequisites

I used shadcn/ui for the date range picker component, date-fns for the date format, and zod for the validation.

shadcn/ui
date-fns
zod

Getting started

Creating challenges on the Lotus project requires attention to detail, especially when setting the start and end dates. To simplify this process for others, I wrote an blog about creating a date range selection component for the challenge creation form.

First, I use Zod to create a new schema called dateRangeSchema. This schema has a date range object with the start date and end date of the form data.

import { z } from zod;
export const dateRangeSchema = z
.object({
// other fields
dateRange: z.object(
{
from: z.date(),
to: z.date(),
},
{
required_error: Please select a date range,
}
),
})
.refine((data) => data.dateRange.from < data.dateRange.to, {
path: [dateRange],
message: From date must be before to date,
});

Now that the Zod schema is ready, I will create the form step by step, starting with the necessary imports.

I have added the dateRangeSchema content to @/zod/schemas/date-range.ts. You can save it in the file if you want but don’t forget to edit it if necessary.

import { cn } from @/lib/utils;
import { useForm } from react-hook-form;
import { zodResolver } from @hookform/resolvers/zod;
import { format } from date-fns;
import { CalendarIcon } from lucide-react;
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from ../ui/form;
import { Popover, PopoverContent, PopoverTrigger } from ../ui/popover;
import { dateRangeSchema } from @/zod/schemas/date-range;

Schema and imports are done. Now I can create the component. When I create the component, I first define defaultValues and resolver using useForm.

defaultValues is a property that sets the initial values of the form fields when the form is first loaded or reset.

zodResolver is a function that converts a Zod schema into a form resolver, ensuring that the form data matches the schema before submission.

export const Component = () => {
const form = useForm({
defaultValues: {
dateRange: {
from: new Date(),
to: new Date(),
},
},
resolver: zodResolver(dateRangeSchema),
});

const onSubmit = (data: z.infer<typeof dateRangeSchema>) => {
console.log(data);
};

return (
// …
);
};

Now everything is ready. Finally, I include the date range picker in Component using shadcn/ui forms.

return (
<Form {…form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name=dateRange
render={({ field }) => (
<FormItem className=flex flex-col>
<FormLabel>Start and End Date</FormLabel>
<Popover modal={true}>
<PopoverTrigger asChild>
<Button
id=date
variant=outline
className={cn(
w-full justify-start text-left font-normal,
!field.value.from && text-muted-foreground
)}
>
<CalendarIcon className=mr-2 h-4 w-4 />
{field.value.from ? (
field.value.to ? (
<>
{format(field.value.from, LLL dd, y)} { }
{format(field.value.to, LLL dd, y)}
</>
) : (
format(field.value.from, LLL dd, y)
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className=w-auto p-0 align=center>
<Calendar
initialFocus
mode=range
defaultMonth={field.value.from}
selected={{
from: field.value.from!,
to: field.value.to,
}}
onSelect={field.onChange}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
<FormDescription>Select the start and end date</FormDescription>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
);

Now we have everything ready. To make the code better,

You can make Date Range Picker a component.
You can improve the onSubmit functionality of the form by using the Sonner package.
You can add custom error messages to the schema.

follow me on X.

Leave a Reply

Your email address will not be published. Required fields are marked *