Forms are tricky. They are one of the most common things you'll build in a web application, but also one of the most complex.
const
form
=
useForm
()
<
FormField
control
={
form
.
control
}
name
=
"username"
render
={({
field
})
=>
(
<
FormItem
>
<
FormLabel
>
Username
</
FormLabel
>
<
FormControl
>
<
Input placeholder
=
"shadcn"
{...
field
}
/>
</
FormControl
>
<
FormDescription
>
This is your public display name.
</
FormDescription
>
<
FormMessage
/>
</
FormItem
>
)}
/>
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
({
username
:
z
.
string
().
min
(2).
max
(50),
})
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
({
username
:
z
.
string
().
min
(2,
{
message
:
"Username must be at least 2 characters."
,
}),
})
export
function
ProfileForm
()
{
//
1. Define your form.
const
form
=
useForm
<
z
.
infer
<typeof
formSchema
>>({
resolver
:
zodResolver
(
formSchema
),
defaultValues
:
{
username
:
""
,
},
})
//
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.
console
.
log
(
values
)
}
}
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.
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
{
Input
}
from
"@/components/ui/input"
const
formSchema
=
z
.
object
({
username
:
z
.
string
().
min
(2,
{
message
:
"Username must be at least 2 characters."
,
}),
})
export
function
ProfileForm
()
{
//
...
return
(
<
Form
{...
form
}>
<
form onSubmit
={
form
.
handleSubmit
(
onSubmit
)}
className
=
"space-y-8"
>
<
FormField
control
={
form
.
control
}
name
=
"username"
render
={({
field
})
=>
(
<
FormItem
>
<
FormLabel
>
Username
</
FormLabel
>
<
FormControl
>
<
Input placeholder
=
"shadcn"
{...
field
}
/>
</
FormControl
>
<
FormDescription
>
This is your public display name.
</
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.