Version: 4.xx.xx
React Hook Form
Refine provides an integration package for React Hook Form library. This package enables you to manage your forms in a headless manner. This adapter supports all of the features of both React Hook Form and Refine's useForm hook. Simply, you can use any of the React Hook Form examples as-is by copying and pasting them into your project.
This package exports following hooks to manage your forms:
Installation
Install the @refinedev/react-hook-form
library.
- npm
- pnpm
- yarn
npm i @refinedev/react-hook-form
pnpm add @refinedev/react-hook-form
yarn add @refinedev/react-hook-form
Usage
Let's see how to edit a post with useForm hook.
- Headless
- Material UI
- Chakra UI
edit.tsx
import { HttpError } from "@refinedev/core";
import { useForm } from "@refinedev/react-hook-form";
export const PostEdit = () => {
const {
refineCore: { onFinish, formLoading, query },
register,
handleSubmit,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: "posts",
action: "edit",
id: 1,
},
});
return (
<form onSubmit={handleSubmit(onFinish)}>
<label>Title: </label>
<input {...register("title", { required: true })} />
{errors.title && <span>This field is required</span>}
<br />
<label>Status: </label>
<select {...register("status")}>
<option value="published">published</option>
<option value="draft">draft</option>
<option value="rejected">rejected</option>
</select>
<br />
<label>Content: </label>
<textarea
{...register("content", { required: true })}
rows={10}
cols={50}
/>
{errors.content && <span>This field is required</span>}
<br />
<input type="submit" value="Submit" />
{formLoading && <p>Loading</p>}
</form>
);
};
export type IStatus = "published" | "draft" | "rejected";
interface IPost {
id: number;
title: string;
content: string;
status: IStatus;
}
edit.tsx
import { HttpError } from "@refinedev/core";
import { Edit } from "@refinedev/mui";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import Autocomplete from "@mui/material/Autocomplete";
import { useForm } from "@refinedev/react-hook-form";
import { Controller } from "react-hook-form";
export const PostEdit: React.FC = () => {
const {
saveButtonProps,
register,
control,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: "posts",
action: "edit",
id: 1,
},
});
return (
<Edit saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column" }}
autoComplete="off"
>
<TextField
id="title"
{...register("title", {
required: "This field is required",
})}
error={!!errors.title}
helperText={errors.title?.message}
margin="normal"
fullWidth
label="Title"
name="title"
autoFocus
/>
<Controller
control={control}
name="status"
rules={{ required: "This field is required" }}
// eslint-disable-next-line
defaultValue={null as any}
render={({ field }) => (
<Autocomplete<IStatus>
id="status"
options={["published", "draft", "rejected"]}
{...field}
onChange={(_, value) => {
field.onChange(value);
}}
renderInput={(params) => (
<TextField
{...params}
label="Status"
margin="normal"
variant="outlined"
error={!!errors.status}
helperText={errors.status?.message}
required
/>
)}
/>
)}
/>
<TextField
id="content"
{...register("content", {
required: "This field is required",
})}
error={!!errors.content}
helperText={errors.content?.message}
margin="normal"
label="Content"
multiline
rows={4}
/>
</Box>
</Edit>
);
};
export type IStatus = "published" | "draft" | "rejected";
export interface IPost {
id: number;
title: string;
content: string;
status: IStatus;
}
edit.tsx
import { HttpError } from "@refinedev/core";
import { useForm } from "@refinedev/react-hook-form";
import { Edit } from "@refinedev/chakra-ui";
import {
FormControl,
FormErrorMessage,
FormLabel,
Input,
Select,
Textarea,
} from "@chakra-ui/react";
export const PostEdit = () => {
const {
refineCore: { formLoading },
saveButtonProps,
register,
formState: { errors },
} = useForm<IPost, HttpError>({
refineCoreProps: {
resource: "posts",
action: "edit",
id: 1,
},
});
return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<FormControl mb="3" isInvalid={!!errors?.title}>
<FormLabel>Title</FormLabel>
<Input
id="title"
type="text"
{...register("title", { required: "Title is required" })}
/>
<FormErrorMessage>{`${errors.title?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mb="3" isInvalid={!!errors?.status}>
<FormLabel>Status</FormLabel>
<Select
id="status"
placeholder="Select Post Status"
{...register("status", {
required: "Status is required",
})}
>
<option>published</option>
<option>draft</option>
<option>rejected</option>
</Select>
<FormErrorMessage>{`${errors.status?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mb="3" isInvalid={!!errors?.content}>
<FormLabel>Content</FormLabel>
<Textarea
id="content"
{...register("content", {
required: "content is required",
})}
/>
<FormErrorMessage>{`${errors.content?.message}`}</FormErrorMessage>
</FormControl>
</Edit>
);
};
export type IStatus = "published" | "draft" | "rejected";
interface IPost {
id: number;
title: string;
content: string;
status: IStatus;
}