iTranslated by AI
How to Use react-hook-form
Hello. I'm Tsu-san, a web designer 😁. Up until now, I've written articles about WordPress and CSS, but thankfully, my front-end work has been increasing lately, so from now on, I'll be writing front-end-related articles as well. I often use React and Next.js for my front-end work.
In this article, I will introduce how to use "react-hook-form," which can be considered a standard library for creating forms in React (Next).
※ Zod, Yup, etc., are not used in this article.
You can also check the source code featured in the article on GitHub.
What is react-hook-form? 🤔
react-hook-form is a library for simplifying and efficiently managing form processing in applications created with React (Next.js). It mainly has the following characteristics:
- Good compatibility with TypeScript
- Excellent performance as it minimizes unnecessary re-renders
- Easy validation configuration
Official site is here → https://react-hook-form.com/
Development Environment 🛠️
Now, let's prepare the development environment. We will use Next.js this time. Detailed versions of Node, Next, etc., are as follows.
Versions of Node, Next, etc.
- Node.js (18.20.4)
- React (^18)
- Next (14.2.5)
- react-hook-form (^7.52.1)
- MUI (5.16.6)
First, create a template for the Next app using create-next-app. I named the app my-react-hook-form.
npx create-next-app
/ /Set as follows
What is your project named? my-react-hook-form
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? No
Would you like to use `src/` directory? Yes
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias (@/*)? No
Next, install react-hook-form.
npm install react-hook-form
Now we are ready.
In this article, we will create a "User Profile Page" for verification. Please refer to the specifications and form input items below.
Specifications
- Validation included. Displays appropriate error messages based on input items.
- Form can be reset.
- An alert is displayed if the user tries to leave the page without submitting after changing form content.
- Submission status and results can be tracked on the UI.
Input Items
| Item | Type | Validation |
|---|---|---|
| Full Name | Text | Required |
| Gender | Select | Required |
| Bio | Textarea | Optional (If entered, must be between 5 and 15 characters) |
| Newsletter | radio | Required |
| Agree to Terms | Checkbox | Required |
Please note that CSS will not be explained here as it is unrelated to react-hook-form. If you need the CSS, please copy and paste it from the link below.
CSS
Basic Usage 🔍
First, here is the basic source code for this project. Since I want to focus on validation behavior here, I've kept it simple without displaying submission status or results.
"use client";
import { useEffect } from "react";
import { Controller, useForm, SubmitHandler } from "react-hook-form";
const Gender = {
empty: "",
female: "female",
male: "male",
other: "other",
} as const; // Make read-only with 'as const'
const NewsLatter = {
receive: "receive",
reject: "reject",
} as const;
// Generate Union types
type Gender = (typeof Gender)[keyof typeof Gender];
type NewsLatter = (typeof NewsLatter)[keyof typeof NewsLatter];
// Generate the type used in the form
type Inputs = {
fullName: string;
gender: Gender;
comment?: string;
newsLatter: NewsLatter;
agree: boolean;
};
export default function Page() {
// Initial values
const defaultValues = {
fullName: "",
gender: Gender.empty,
comment: "",
newsLatter: NewsLatter.receive,
agree: false,
};
// react-hook-form initial settings
const { control, handleSubmit, reset } = useForm<Inputs>({
defaultValues,
});
// Form submission process
const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data);
// Form reset process
const handleReset = () => {
reset();
};
return (
<div>
<p className="breadcrumb">[ basic ]</p>
<form onSubmit={handleSubmit(onSubmit)} className="form">
<Controller
name="fullName"
rules={{ required: "It seems the name hasn't been entered." }}
control={control}
render={({ field, fieldState }) => (
<div
className={`form-group ${!!fieldState.error ? `is-error` : ``}`}
>
<label htmlFor="fullName">Name</label>
<div>
<input id="fullName" type="text" {...field} />
{fieldState.error && (
<span className="error-message">
{fieldState.error.message}
</span>
)}
</div>
</div>
)}
/>
<Controller
name="gender"
rules={{ required: "It seems the gender hasn't been selected." }}
control={control}
render={({ field, fieldState }) => (
<div
className={`form-group ${!!fieldState.error ? `is-error` : ``}`}
>
<label htmlFor="gender">Gender</label>
<div>
<select id="gender" {...field}>
<option value={Gender.empty}></option>
<option value={Gender.female}>Female</option>
<option value={Gender.male}>Male</option>
<option value={Gender.other}>No Answer</option>
</select>
{fieldState.error && (
<span className="error-message">
{fieldState.error.message}
</span>
)}
</div>
</div>
)}
/>
<Controller
name="comment"
rules={{
minLength: { value: 3, message: "Please enter at least 3 characters" },
maxLength: { value: 10, message: "Please enter no more than 10 characters" },
}}
control={control}
render={({ field, fieldState }) => (
<div
className={`form-group ${!!fieldState.error ? `is-error` : ``}`}
>
<label htmlFor="comment">Comment</label>
<div>
<textarea id="comment" {...field} />
{fieldState.error && (
<span className="error-message">
{fieldState.error.message}
</span>
)}
</div>{" "}
</div>
)}
/>
<Controller
name="newsLatter"
rules={{
required: "Please select whether to receive notifications",
}}
control={control}
render={({ field, fieldState }) => (
<div
className={`form-group ${!!fieldState.error ? `is-error` : ``}`}
>
<label htmlFor="newsLatter">Newsletter</label>
<div>
<div className="radio-group">
<label>
<input
type="radio"
{...field}
value={NewsLatter.receive}
checked={field.value === NewsLatter.receive}
/>
Receive
</label>
<label>
<input
type="radio"
{...field}
value={NewsLatter.reject}
checked={field.value === NewsLatter.reject}
/>
Do not receive
</label>
</div>
{fieldState.error && (
<span className="error-message">
{fieldState.error.message}
</span>
)}
</div>
</div>
)}
/>
<Controller
name="agree"
rules={{ required: "Please check the checkbox" }}
control={control}
render={({ field: { value, ...rest }, fieldState }) => (
<div
className={`form-group ${!!fieldState.error ? `is-error` : ``}`}
>
<div>
<label>
<input type="checkbox" checked={value} {...rest} />{" "}
<span>Agree to Terms of Use</span>
</label>
{fieldState.error && (
<span className="error-message">
{fieldState.error.message}
</span>
)}
</div>
</div>
)}
/>
<div className="button-group">
<button type="submit">Submit</button>
<button type="button" onClick={handleReset}>
Reset
</button>
</div>
</form>
</div>
);
}
Operation Check
Start the development server with npm run dev to verify the operation.
npm run dev
Once it's running, try submitting the form, resetting it, and testing the validation. Below are captures of validation errors and the submission process.

Validation check

Submit check (displayed in the console)
It's working as intended, so there are no issues. Now, I'll explain the details.
react-hook-form Initial Settings
First is the initial setup. By specifying the type with useForm<Inputs>, you can manage the form state based on the data structure predefined in type Inputs. At the same time, set initial values with defaultValues.
// Initial values
const defaultValues = {
fullName: "",
gender: Gender.empty,
comment: "",
newsLatter: NewsLatter.receive,
agree: false,
};
// react-hook-form initial settings
const { control, handleSubmit, reset } = useForm<Inputs>({
defaultValues,
});
And you receive "control, handleSubmit, reset" from useForm. These will be used later.
| Name | Description |
|---|---|
| control | Used to register each form field with react-hook-form and manage their states. |
| handleSubmit | A function that manages the form submission process. |
| reset | A function that resets the form state. |
Form Submission and Reset
Next, create functions for form submission and reset. The submission function (onSubmit) is specified as an argument to handleSubmit, like <form onSubmit={handleSubmit(onSubmit)}>. Since we only want to check the validation for now, we'll just display the submitted data in the console.
// Form submission process
const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data);
// Form reset process (returns to defaultValue values)
const handleReset = () => {
reset();
};
Validation for Each Input Item
Now, let's look at the crucial validation part. We will create the form components using the Controller component imported from react-hook-form.
A quick supplement before we continue. While it's possible to create fields without the Controller component by writing something like <input defaultValue="test" {...register("fullName")} />, we are using the Controller component this time to consider the possibility of combining it with other UI libraries.
If you prefer the {...register} style, please refer to the following:
https://react-hook-form.com/get-started
Let's explain using a simple <input type="text" /> as an example.
<Controller
name="fullName" // Must exist within type Inputs
rules={{ required: "It seems the name hasn't been entered." }} // Text for validation error
control={control}
render={({ field, fieldState }) => (
// Add a class for errors using !!fieldState.error
<div className={`form-group ${!!fieldState.error ? `is-error` : ``}`}>
<label htmlFor="fullName">Name</label>
<div>
<input id="fullName" type="text" {...field} /> // Spread the field
{fieldState.error && (
<span className="error-message">
{fieldState.error.message} // Error message
</span>
)}
</div>
</div>
)}
/>
Below is an explanation of the Controller component's props.
| props | Value | Description |
|---|---|---|
| name | "fullName", "gender", "comment"... | Must exist within type Inputs. An error will occur if it doesn't. |
| rules | required, maxLength, minLength... | Validation settings. You can set requirements or the minimum/maximum length. |
| control | control | By setting control, it becomes managed by react-hook-form. |
| render | DOM | Set the actual DOM to be displayed (rendered). |
Next, I'll explain { field, fieldState } in the render prop.
field contains name, value, onChange, onBlur, and ref, which we spread for use.
const { name, value, onChange, onBlur, ref } = field;
/** Spread and use */
// <input type="text" {...field} />
/** Example without spreading */
// <input type="text" name={name} value={value} onChange={onChange} onBlur={onBlur} ref={ref} />
fieldState contains inValid, isTouched, isDirty, and error.
| Name | Description |
|---|---|
| inValid | Whether an error has been detected |
| isTouched | Whether it has been interacted with (focus / blur event) |
| isDirty | Whether it has been modified |
| error | Error information |
How to use field with checkboxes and radio buttons
While <input type="text" /> and <textarea> use {...field}, the notation for checkbox and radio buttons is slightly different. I'd like to explain this point.
About checkboxes
If you use <input type="checkbox" {...field} />, you will likely get a type check error in your editor like this:
Type '{ onChange: (...event: any[]) => void; onBlur: Noop; value: boolean; disabled?: boolean | undefined; name: "agree"; ref: RefCallBack; type: "checkbox"; }' is not assignable to type 'DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>'.
Type '{ onChange: (...event: any[]) => void; onBlur: Noop; value: boolean; disabled?: boolean | undefined; name: "agree"; ref: RefCallBack; type: "checkbox"; }' is not assignable to type 'InputHTMLAttributes<HTMLInputElement>'.
The types of property 'value' are incompatible.
Type 'boolean' is not assignable to type 'string | number | readonly string[] | undefined'.
Focus on this message:
Type 'boolean' is not assignable to type 'string | number | readonly string[] | undefined'.
This is because the expected type for the value of <input type="checkbox"> is "string | number | readonly string[] | undefined", while the type of value provided by react-hook-form is "boolean," causing a type mismatch.
In such cases, you can solve it by separating value and other properties (rest) as shown below:
// Separate properties into value and rest. value becomes true only when checked.
<input type="checkbox" checked={value} {...rest} />
About radio buttons
Writing <input type="radio" {...field} /> won't show any specific error in the editor. However, in that case, both radio buttons would have the value set in defaultValues, which is NewsLatter.receive.
const defaultValues = {
fullName: "",
gender: Gender.empty,
comment: "",
newsLatter: NewsLatter.receive,
agree: false,
};
Therefore, we "overwrite the value" using value={NewsLatter.receive} after spreading with {...field}, as shown below. Then, we reflect the selection status in the UI with checked={field.value === NewsLatter.receive}.
<label>
<input
type="radio"
{...field}
value={NewsLatter.receive} // Overwrite value to "receive"
checked={field.value === NewsLatter.receive}
/>
Receive
</label>
<label>
<input
type="radio"
{...field}
value={NewsLatter.reject} // Overwrite value to "reject"
checked={field.value === NewsLatter.reject}
/>
Do not receive
</label>
That's it for the supplement on checkboxes and radio buttons.
Customizing the form for better usability 👍
The previous form allowed submission and resetting, but since it only had minimal features, it wasn't exactly user-friendly. I'd like to add the following points to make the form easier to use.
- Disable the "Submit" button when there are validation errors (disabled)
- Display an alert when trying to leave the page before submitting (beforeunload)
- Reflect form submission status and results in the UI
- Allow retrying if form submission fails
Changing react-hook-form configuration
First, change the react-hook-form settings to retrieve submission and validation statuses.
// react-hook-form initial settings
const {
control,
handleSubmit,
reset,
+ setError,
+ clearErrors,
+ formState: {
+ isDirty,
+ isValid,
+ isSubmitting,
+ isSubmitted,
+ isSubmitSuccessful,
+ errors,
+ },
} = useForm<Inputs>({
defaultValues,
+ mode: "all",
});
Various settings have been added besides "control, handleSubmit, and reset."
| Name | Description |
|---|---|
| setError | Allows setting an error manually |
| clearErrors | Clears errors |
| isDirty | Whether form content has changed (default: false) |
| isValid | Validation verification result (default: false) |
| isSubmitting | Whether the form is being submitted (default: false) |
| isSubmitted | Whether the form has been submitted (default: false) |
| isSubmitSuccessful | Whether the form was submitted successfully (default: false) |
| errors | Error information |
Notice that "mode" has been added under defaultValues. In "mode", you can specify when to perform validation checks.
| Name | Description |
|---|---|
| onSubmit | When the form is submitted (default) |
| onChange | When input element content changes |
| onBlur | When the input element loses focus |
| all | All of the above |
We are specifying "all" this time, but please be careful as it may degrade performance if the form has too many components.
Changing form submission processing
Next, change the form submission process. To simulate success and failure, I'm using const isSuccess = Math.random() < 0.5; to execute success and failure randomly.
When submitting the form, first clear error content with clearErrors("root"), and in case of submission failure, set an error using setError inside the catch block.
By being able to catch errors, we can now track the success and failure of form submission.
This allows us to display an appropriate UI according to the situation.
// Form submission process
- const onSubmit: SubmitHandler<Inputs> = (data) => console.log(data);
+ const onSubmit: SubmitHandler<Inputs> = async (data) => {
+ clearErrors("root");
+ try {
+ await new Promise<void>((resolve, reject) => {
+ setTimeout(() => {
+ // Execute success and failure randomly
+ const isSuccess = Math.random() < 0.5;
+ if (isSuccess) {
+ console.log("Submission success:", data);
+ // reset(); Normally, reset the form after successful submission
resolve();
} else {
console.log("Submission failure");
reject(new Error("Submission failed"));
}
}, 1500);
});
} catch (error) {
console.error("Error:", error);
setError("root.serverError", {
type: "manual",
message: "Submission failed. Please try again.",
});
}
};
Prevent clicking the "Submit" button when there are validation errors (disabled)
Add disabled to the submit button to control clicks. By adding isDirty and isValid, if either is false, disabled will be applied and the button cannot be clicked.
- <button type="submit">送信</button>
+ <button type="submit" disabled={!isDirty || !isValid}>送信</button>
Display an alert when trying to leave the page before submitting (beforeunload)
Next, use beforeunload to add processing for when a user tries to leave the page before submitting.
// Form reset process
const handleReset = () => {
reset();
};
+ // Alert for leaving before saving form
+ useEffect(() => {
+ const handleBeforeUnload = (e: BeforeUnloadEvent) => { // BeforeUnloadEvent is a TypeScript type definition, no import needed
+ if (isDirty) {
+ e.preventDefault();
+ }
+ };
+
+ window.addEventListener("beforeunload", handleBeforeUnload);
+
+ return () => {
+ window.removeEventListener("beforeunload", handleBeforeUnload);
+ };
+ }, [isDirty]);
Specify isDirty in the useEffect dependency array and prevent the default behavior of the beforeunload event only when it is true. As a result, an alert will be displayed if a user tries to "leave the page after the form content has been changed but not submitted."
Display form submission status and results
Since we added "isSubmitting, isSubmitted, isSubmitSuccessful" earlier, we will use them to display the form submission status and results in the UI.
<form onSubmit={handleSubmit(onSubmit)} className="form">
+ {isSubmitting && <div className="result">Submitting</div>}
+ {!isSubmitting && isSubmitted && (
+ <div className={`result ${isSubmitSuccessful ? `is-success` : `is-failed`}`}>
+ {isSubmitSuccessful ? "Submission successful" : "Submission failed"}
+ </div>
+ )}
<Controller
name="fullName"
Allow retrying if form submission fails
Next, add a retry button so that if form submission fails, it can be resent. We used setError in the catch block of the onSubmit function earlier to set an error. We'll use that here.
{!isSubmitting && isSubmitted && (
<div className={`result ${ isSubmitSuccessful ? `is-success` : `is-failed` }`}>
{isSubmitSuccessful ? "Submission successful" : "Submission failed"}
+ {errors.root?.serverError && (
+ <button onClick={() => handleSubmit(onSubmit)}>Retry</button>
+ )}
</div>
)}
Once you've done this, run npm run dev and check the operation again.
npm run dev
Below are screenshots of the actual UI.

Display during submission

Display on submission failure

Display on submission success
:::
Validation Check Timing and isValid 😵💫
By default, the timing for react-hook-form to perform validation checks is onSubmit (when the form is submitted).
While this usually works fine, you might want to be careful when setting disabled={!isValid} on the submit button. Let me explain why. Take a look at the image below.

"Name, Gender, Comment, Newsletter, Agree to Terms" are all set, but the "Submit button" cannot be pressed. No error message is displayed either. Why is that?
Actually, because a validation rule of "at least 3 characters for the comment" is applied, and in the image above there are only 2 characters, a validation error is occurring.
Since there is a validation error, isValid remains false, the disabled state of the submit button is not released, and it cannot be clicked.
// isValid remains false
<button type="submit" disabled={!isValid}>
Submit
</button>
And because the submit button cannot be clicked, it ends up in a state where "the validation error message is never displayed."
In this state, users will be troubled, unable to find even a clue to the solution of "Why can't I press the submit button?"
In such cases, you can solve this by changing the validation check timing to "mode" like onChange or onBlur.
const {
control,
handleSubmit,
reset,
formState: { isValid },
} = useForm<Inputs>({
defaultValues,
// When the content is changed, perform validation check and display error messages immediately
+ mode: "onChange"
});
The combination of validation check timing and validation rules may potentially confuse users, so be sure to always check the behavior when validation errors occur.
Using MUI to Enhance Appearance 🐦
Previously, we created the form using plain HTML. Now, let's try creating a form in combination with MUI (Material UI).
Note: There are no changes to the specifications, configuration items, or validation rules.
Please refer to the link below for MUI installation instructions.
https://mui.com/material-ui/getting-started/installation/
Source Code
"use client";
import { useEffect } from "react";
import { Controller, useForm, SubmitHandler } from "react-hook-form";
import {
TextField,
Select,
MenuItem,
FormControl,
InputLabel,
FormControlLabel,
Radio,
RadioGroup,
Checkbox,
Button,
Typography,
Box,
Container,
CircularProgress,
Stack,
Alert,
} from "@mui/material";
const Gender = {
empty: "",
female: "female",
male: "male",
other: "other",
} as const;
const NewsLatter = {
receive: "receive",
reject: "reject",
} as const;
type Gender = (typeof Gender)[keyof typeof Gender];
type NewsLatter = (typeof NewsLatter)[keyof typeof NewsLatter];
type Inputs = {
fullName: string;
gender: Gender;
comment?: string;
newsLatter: NewsLatter;
agree: boolean;
};
export default function Page() {
const defaultValues = {
fullName: "",
gender: Gender.empty,
comment: "",
newsLatter: NewsLatter.receive,
agree: false,
};
const {
control,
handleSubmit,
reset,
setError,
clearErrors,
formState: {
isDirty,
isValid,
isSubmitting,
isSubmitted,
isSubmitSuccessful,
errors,
},
} = useForm<Inputs>({
defaultValues,
mode: "all",
});
const onSubmit: SubmitHandler<Inputs> = async (data) => {
clearErrors("root");
try {
await new Promise<void>((resolve, reject) => {
setTimeout(() => {
const isSuccess = Math.random() < 0.5;
if (isSuccess) {
console.log("Submission successful:", data);
resolve();
} else {
console.log("Submission failed");
reject(new Error("Submission failed"));
}
}, 1500);
});
} catch (error) {
console.error("Error:", error);
setError("root.serverError", {
type: "manual",
message: "Submission failed. Please try again.",
});
}
};
const handleReset = () => {
reset();
};
useEffect(() => {
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
if (isDirty) {
e.preventDefault();
}
};
window.addEventListener("beforeunload", handleBeforeUnload);
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
};
}, [isDirty]);
useEffect(() => {
console.table([
{
Status: "Form status",
isDirty,
isValid,
isSubmitting,
isSubmitted,
isSubmitSuccessful,
},
]);
console.table(
Object.entries(errors).map(([key, value]) => ({
Field: key,
ErrorMessage:
value && typeof value === "object" && "message" in value
? value.message
: JSON.stringify(value),
}))
);
}, [errors, isDirty, isSubmitSuccessful, isSubmitted, isSubmitting, isValid]);
return (
<Container>
<Typography variant="body2" sx={{ textAlign: "right", mb: 4 }}>
[ MUI ]
</Typography>
<Box
component="form"
onSubmit={handleSubmit(onSubmit)}
noValidate
sx={{ mt: 1 }}
>
{isSubmitting && (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
my: 2,
}}
>
<CircularProgress size={40} sx={{ mr: 2 }} />
<Typography variant="body1">Uploading...</Typography>
</Box>
)}
{!isSubmitting && isSubmitted && (
<Alert
severity={isSubmitSuccessful ? "success" : "error"}
sx={{ alignItems: "center" }}
>
{isSubmitSuccessful ? "Submission successful" : "Submission failed"}
{errors.root?.serverError && (
<Button onClick={() => handleSubmit(onSubmit)()}>Retry</Button>
)}
</Alert>
)}
<Controller
name="fullName"
control={control}
rules={{ required: "It seems the name hasn't been entered." }}
render={({ field, fieldState: { error } }) => (
<Box>
<TextField
{...field}
margin="normal"
required
fullWidth
id="fullName"
label="Name"
error={!!error}
helperText={error?.message}
/>
</Box>
)}
/>
<Controller
name="gender"
control={control}
rules={{ required: "It seems the gender hasn't been selected." }}
render={({ field, fieldState: { error } }) => (
<Box>
<FormControl fullWidth margin="normal" error={!!error}>
<InputLabel id="gender-label">Gender</InputLabel>
<Select
{...field}
labelId="gender-label"
id="gender"
label="Gender"
>
<MenuItem value={Gender.empty}>
<em>Please select</em>
</MenuItem>
<MenuItem value={Gender.female}>Female</MenuItem>
<MenuItem value={Gender.male}>Male</MenuItem>
<MenuItem value={Gender.other}>No Answer</MenuItem>
</Select>
{error && (
<Typography color="error">{error.message}</Typography>
)}
</FormControl>
</Box>
)}
/>
<Controller
name="comment"
control={control}
rules={{
minLength: { value: 3, message: "Please enter at least 3 characters" },
maxLength: { value: 10, message: "Please enter no more than 10 characters" },
}}
render={({ field, fieldState: { error } }) => (
<Box>
<TextField
{...field}
margin="normal"
fullWidth
id="comment"
label="Comment"
multiline
rows={4}
error={!!error}
helperText={error?.message}
/>
</Box>
)}
/>
<Controller
name="newsLatter"
control={control}
rules={{ required: "Please select whether to receive notifications" }}
render={({ field, fieldState: { error } }) => (
<Box>
<FormControl component="fieldset" margin="normal" error={!!error}>
<Typography component="legend">Newsletter</Typography>
<RadioGroup {...field} row>
<FormControlLabel
value={NewsLatter.receive}
control={<Radio />}
label="Receive"
/>
<FormControlLabel
value={NewsLatter.reject}
control={<Radio />}
label="Do not receive"
/>
</RadioGroup>
{error && (
<Typography color="error">{error.message}</Typography>
)}
</FormControl>
</Box>
)}
/>
<Controller
name="agree"
control={control}
rules={{ required: "Please check the checkbox" }}
render={({ field, fieldState: { error } }) => (
<FormControlLabel
control={
<Checkbox {...field} checked={field.value} color="primary" />
}
label="Agree to Terms of Use"
/>
)}
/>
{errors.agree && (
<Typography color="error">{errors.agree.message}</Typography>
)}
<Box sx={{ mt: 10, display: "flex", justifyContent: "flex-end" }}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={!isDirty || !isValid}
sx={{ mr: 2 }}
>
Submit
</Button>
<Button type="button" variant="outlined" onClick={handleReset}>
Reset
</Button>
</Box>
</Box>
</Container>
);
}
Just like with plain HTML, we are incorporating MUI components into the render part of the Controller component.
When combining react-hook-form with other libraries like this, you will end up using the Controller component. For this reason, I personally prefer to unify the approach by using the Controller component without using {...register}, as it minimizes the scope of modifications needed.
This concludes the introduction on how to use react-hook-form. Thank you for reading to the end. I hope this article is helpful to everyone.
Discussion
タイトルと本文のタイポが少し気になったので共有させていただきます。
誤り) raect-hook-form
正解) react-hook-form
ありがとうございます。修正しました。