This guide demonstrates how to integrate Emblor's TagInput component into a shadcn/ui form, which utilizes react-hook-form and zod for validation and state management.
Prerequisites
- A project with
shadcn/ui installed. react-hook-form and zod installed.
Installation
- Install the shadcn/ui Form component:
npx shadcn-ui@latest add form
- Install Emblor:
npm install emblor
Implementation Steps
1. Create a form schema
Define the shape of your form using a Zod schema. For a tag input, the schema should expect an array of objects containing id and text.
2. Define a form
Use the useForm hook from react-hook-form with the zodResolver. Because FormField uses controlled components, you must provide defaultValues (e.g., an empty array for tags).
3. Build your form
Wrap your form in the <Form> provider and use <FormField> to render the TagInput. To sync the TagInput state with react-hook-form, use the setTags prop to call setValue from the form instance.
Anatomy of the Integration
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="topics"
render={({ field }) => (
<FormItem>
<FormLabel>Topics</FormLabel>
<FormControl>
<TagInput
{...field}
tags={tags}
setTags={(newTags) => {
setTags(newTags);
setValue('topics', newTags);
}}
/>
</FormControl>
<FormDescription>Description text</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
import { 1. Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Button } from '@/components/ui/button';
import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import React from 'react';
import { Tag, TagInput } from 'emblor';
const FormSchema = z.object({
topics: z.array(
z.object({
id: z.string(),
text: z.string(),
}),
),
});
export default function Demo() {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
topics: [],
},
});
const [tags, setTags] = React.useState<Tag[]>([]);
const { setValue } = form;
function onSubmit(data: z.infer<typeof FormSchema>) {
console.log(data);
}
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>
);
}