Standard stack
Every FormField should render FormMessage. Add react-hook-form, zod, and @hookform/resolvers to frontend/package.json.
- Form — spreads the useForm return value
- FormField — binds control + name via render prop
- FormItem / FormLabel / FormControl / FormMessage
- zod schema + zodResolver for validation
TSXform
1import { zodResolver } from "@hookform/resolvers/zod";
2import {
3 Button,
4 Form,
5 FormControl,
6 FormField,
7 FormItem,
8 FormLabel,
9 FormMessage,
10 Input,
11} from "@quan-erp/shared-ui";
12import { useForm } from "react-hook-form";
13import { z } from "zod";
14
15const schema = z.object({
16 email: z.string().min(1, "Email is required").email("Enter a valid email"),
17 password: z.string().min(6, "Password must be at least 6 characters"),
18});
19
20type FormValues = z.infer<typeof schema>;
21
22export function ExampleForm({ onSubmit }: { onSubmit: (v: FormValues) => void }) {
23 const form = useForm<FormValues>({
24 resolver: zodResolver(schema),
25 defaultValues: { email: "", password: "" },
26 });
27
28 return (
29 <Form {...form}>
30 <form className="space-y-3" onSubmit={form.handleSubmit(onSubmit)}>
31 <FormField
32 control={form.control}
33 name="email"
34 render={({ field }) => (
35 <FormItem>
36 <FormLabel>Email</FormLabel>
37 <FormControl>
38 <Input type="email" autoComplete="email" {...field} />
39 </FormControl>
40 <FormMessage />
41 </FormItem>
42 )}
43 />
44 <Button type="submit">Submit</Button>
45 </form>
46 </Form>
47 );
48}