- Published on
Shadcn Input with Form and Zod: Resolving the Uncontrolled Input Warning
- Authors
- Name
- Ripal & Zalak
Shadcn Input with Form and Zod: Resolving the Uncontrolled Input Warning
When building forms with Shadcn UI, React Hook Form, and Zod, you might encounter this warning:
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen.
This error occurs when the value
of an input field transitions from undefined
to a defined value. Although the form may still work, this warning indicates a potential issue with your code. Let’s explore the cause and how to fix it.
The Problem
In React Hook Form (RHF), fields that don’t have defaultValues
start with an undefined
value. When you type into such fields, the value changes from undefined
to a string, triggering the warning.
Here’s an example of code that causes this issue:
<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>
)}
/>
The field
object from RHF doesn’t initialize a value
, causing it to be undefined
initially. The solution is to define defaultValues
in your useForm
configuration.
The Solution
useForm
1. Set Default Values in Adding defaultValues
to your useForm
configuration ensures that all fields are initialized with a value, avoiding the transition from undefined
to a string.
const form = useForm({
resolver: zodResolver(FormSchema),
defaultValues: {
username: '',
email: '',
},
})
2. Use Controlled Inputs
Ensure all inputs are properly controlled by React Hook Form. Here’s the updated implementation for the username field:
<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>
)}
/>
With defaultValues
set, the field.value
is no longer undefined
, preventing the warning.
3. Handling Dynamic Data
If your form data comes from an API or is dynamic, use the reset
method provided by React Hook Form to initialize the form values after data is loaded.
Example:
useEffect(() => {
if (data) {
form.reset(data)
}
}, [data, form])
This ensures that the form values are populated correctly when the data is available.
Complete Example
Here’s a complete implementation of a form with Shadcn UI, React Hook Form, and Zod:
'use client'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import * as z from 'zod'
import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { toast } from '@/components/ui/use-toast'
const FormSchema = z.object({
email: z.string().email(),
username: z.string(),
})
const ZodForm = () => {
const form = useForm({
resolver: zodResolver(FormSchema),
defaultValues: {
email: '',
username: '',
},
})
function onSubmit(data) {
console.log(data)
toast({
title: 'Form Submitted',
description: JSON.stringify(data, null, 2),
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select your email" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="[email protected]">[email protected]</SelectItem>
<SelectItem value="[email protected]">[email protected]</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="shadcn" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<button type="submit">Submit</button>
</form>
</Form>
)
}
export default ZodForm
Conclusion
The warning about uncontrolled inputs can be easily resolved by setting defaultValues
in your React Hook Form configuration. This ensures that all fields have initial values, preventing the transition from undefined
to a defined value. For dynamic data, use the reset
method to populate form values after data is loaded.
By following these practices, you can build robust and error-free forms using Shadcn UI, React Hook Form, and Zod.