In this guide, we will take a look at building forms with shadcn/ui form component which uses react-hook-form and zod .
Anatomy
< Form >
< FormField
control = { ... }
name = "..."
render = {() => (
< FormItem >
< FormLabel />
< FormControl >
{ /* tag input goes here */ }
</ FormControl >
< FormDescription />
< FormMessage />
</ FormItem >
)}
/>
</ Form >
Installation
Install the Form component by running the following command:
npx shadcn-ui@latest add form
Install Emblor by running the following command:
npm install emblor
# Or, use any package manager of your choice.
Usage
Create a form schema
Define the shape of your form using a Zod schema. You can read more about using Zod in the Zod documentation .
'use client' ;
import { z } from 'zod' ;
const FormSchema = z. object ({
topics: z. array (
z. object ({
id: z. string (),
text: z. string (),
}),
),
});
Define a form
Use the useForm hook from react-hook-form to create a form.
'use client' ;
import { zodResolver } from '@hookform/resolvers/zod' ;
import { useForm } from 'react-hook-form' ;
import { z } from 'zod' ;
const FormSchema = z. object ({
topics: z. array (
z. object ({
id: z. string (),
text: z. string (),
}),
),
});
export function Form () {
// 1. Define your form.
const form = useForm < z . infer < typeof FormSchema>>({
resolver: zodResolver (FormSchema),
defaultValues: {
topics: [],
},
});
// 2. Define a submit handler.
function onSubmit ( data : z . infer < typeof FormSchema>) {
toast ({
title: 'You submitted the following values:' ,
description : (
< pre className = "mt-2 w-[340px] rounded-md bg-slate-950 p-4" >
< code className = "text-white" >{ JSON . stringify (data, null , 2 )}</ code >
</ pre >
),
});
}
}
Since FormField is using a controlled component, you need to provide a default value for the field. See the React Hook Form docs to learn more about controlled components.
Build your form
We can now use the Form components to build our form.
'use client' ;
import { zodResolver } from '@hookform/resolvers/zod' ;
import { useForm } 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 { Tag, TagInput } from 'emblor' ;
const FormSchema = z. object ({
topics: z. array (
z. object ({
id: z. string (),
text: z. string (),
}),
),
});
export function Form () {
// ...
const [ tags , setTags ] = React. useState < Tag []>([]);
const { setValue } = form;
return (
< Form { ... form}>
< form onSubmit = {form. handleSubmit (onSubmit)} className = "space-y-8 flex flex-col items-start" >
< FormField
control = {form.control}
name = "topics"
render = {({ field }) => (
< FormItem className = "flex flex-col items-start" >
< FormLabel className = "text-left" >Topics</ FormLabel >
< FormControl className = "w-full" >
< TagInput
{ ... field}
placeholder = "Enter a topic"
tags = {tags}
className = "sm:min-w-[450px]"
setTags = {( newTags ) => {
setTags (newTags);
setValue ( 'topics' , newTags as [ Tag , ... Tag []]);
}}
/>
</ FormControl >
< FormDescription className = "text-left" >
These are the topics that you ' re interested in.
</ FormDescription >
< FormMessage />
</ FormItem >
)}
/>
< Button type = "submit" >Submit</ Button >
</ form >
</ Form >
);
}
Done
That's it. You now have a fully accessible form that is type-safe with client-side validation.