React Hook Form is optimized for uncontrolled components using {...register}, but it is fully compatible with controlled components.
- Uncontrolled Components: Use components that forward a
ref (like a standard <input />) and register them directly with {...register('name')}. - Controlled Components: For UI library components that do not expose a native input ref (e.g., MUI
Select, Antd Checkbox), wrap them with the Controller component. The Controller manages the component's state and provides a field object containing onChange, onBlur, value, and ref to be spread onto the component.
You can mix both patterns within a single form.
import { Input, Select, MenuItem } from "@material-ui/core"
import { useForm, Controller } from "react-hook-form"
const defaultValues = {
select: "",
input: "",
}
function App() {
const { handleSubmit, reset, control, register } = useForm({
defaultValues,
})
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
render={({ field }) => (
<Select {...field}>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
</Select>
)}
control={control}
name="select"
defaultValue={10}
/>
<Input {...register("input")} />
<button type="button" onClick={() => reset({ ...defaultValues })}>
Reset
</button>
<input type="submit" />
</form>
)
}