Install Form by Shadcn
>_ Terminal
npx shadcn-ui@latest add form input
>_ Terminal
npx shadcn-ui@latest add form input
Note that everything is in the same file for demo purpose. You should divide this in multiple files for a better DX.
form.tsx
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { FC } from "react";
import {
Control,
FieldPath,
FieldValues,
useForm,
useFormContext,
} from "react-hook-form";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input, InputProps } from "@/components/ui/input";
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const formSchema = z
.object({
username: z.string().min(5, {
message: "Le nom d'utilisateur est trop court.",
}),
email: z.string().email({ message: "Ce champ doit être un email valide." }),
age: z.coerce
.number({ message: "Vous devez rentrer un chiffre." })
.min(13, { message: "Age minimum : 13 ans" }),
password: z
.string()
.min(8, { message: "Le mot de passe doit faire au moins 8 caractères" }),
passwordConfirm: z
.string()
.min(8, { message: "Le mot de passe doit faire au moins 8 caractères." }),
})
.refine((data) => data.password === data.passwordConfirm, {
message: "Les mots de passes sont différents.",
path: ["passwordConfirm"],
});
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
type FormInputProps<T extends FieldValues> = {
control?: Control<T>;
name: FieldPath<T>;
type: InputProps["type"];
label?: string;
description?: string;
};
const FormInput = <T extends FieldValues>({
control,
name,
type,
label,
description,
}: FormInputProps<T>) => {
const { control: defaultControl } = useFormContext<T>();
return (
<FormField
control={control || defaultControl}
name={name}
render={({ field }) => (
<FormItem>
{!!label && <FormLabel>{label}</FormLabel>}
<FormControl>
<Input type={type} {...field} />
</FormControl>
{!!description && <FormDescription>{description}</FormDescription>}
<FormMessage />
</FormItem>
)}
/>
);
};
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const Formulaire: FC = () => {
// 1. Define your form.
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
email: "",
age: undefined,
password: "",
passwordConfirm: "",
},
});
// 2. Define a submit handler.
function onSubmit(values: z.infer<typeof formSchema>) {
// Do something with the form values.
// This will be type-safe and validated.
alert(JSON.stringify(values, null, 2));
}
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-3 container py-6"
>
<FormInput
control={form.control}
name={"username"}
label={"Username"}
type="string"
/>
<FormInput
control={form.control}
name={"email"}
label={"Email"}
type="email"
/>
<FormInput
control={form.control}
name={"age"}
label={"Age"}
type="number"
/>
<FormInput
control={form.control}
name={"password"}
label={"Password"}
type="password"
/>
<FormInput
control={form.control}
name={"passwordConfirm"}
label={"Confirm Password"}
type="password"
/>
<Button type="submit">Submit</Button>
</form>
</Form>
);
};
export default Formulaire;