Core Form Hook
TheuseForm hook from @refinedev/core orchestrates all form operations:
Quick Start
@refinedev/react-hook-form - React Hook Form integration@refinedev/antd - Ant Design forms@refinedev/mui - Material UI forms@refinedev/mantine - Mantine formsimport { useForm } from "@refinedev/react-hook-form";
import { useSelect } from "@refinedev/core";
export const PostCreate = () => {
const {
refineCore: { onFinish, formLoading },
register,
handleSubmit,
formState: { errors },
} = useForm();
const { options } = useSelect({
resource: "categories",
});
return (
<form onSubmit={handleSubmit(onFinish)}>
<label>Title</label>
<input {...register("title", { required: "Title is required" })} />
{errors.title && <span>{errors.title.message}</span>}
<label>Content</label>
<textarea {...register("content", { required: true })} />
{errors.content && <span>Content is required</span>}
<label>Category</label>
<select {...register("category.id", { required: true })}>
<option value="">Select...</option>
{options?.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
{errors.category && <span>Category is required</span>}
<label>Status</label>
<select {...register("status")}>
<option value="draft">Draft</option>
<option value="published">Published</option>
</select>
<button type="submit" disabled={formLoading}>
{formLoading ? "Saving..." : "Save"}
</button>
</form>
);
};
import { useForm } from "@refinedev/react-hook-form";
export const PostEdit = () => {
const {
refineCore: { onFinish, formLoading, queryResult },
register,
handleSubmit,
formState: { errors },
} = useForm();
const isLoading = queryResult?.isLoading || formLoading;
if (isLoading) return <div>Loading...</div>;
return (
<form onSubmit={handleSubmit(onFinish)}>
<label>Title</label>
<input {...register("title", { required: true })} />
{errors.title && <span>Required</span>}
<label>Content</label>
<textarea {...register("content")} />
<button type="submit" disabled={formLoading}>
Update
</button>
</form>
);
};
Form Actions
Create
For creating new records:Edit
For updating existing records:Clone
For duplicating records:Form Libraries Integration
React Hook Form
Full-featured with great performance:Ant Design Forms
Native Ant Design integration:Material UI Forms
Material-UI with React Hook Form:Mantine Forms
Native Mantine integration:Validation
Client-Side Validation
Server-Side Validation
Handle validation errors from the API:Custom Validation
Advanced Features
Auto-Save
Automatically save form changes:Mutation Modes
Control optimistic updates and undo behavior:Query Invalidation
Control which queries to refresh after mutation:Redirection
Customize post-submit navigation:Notifications
Customize success and error notifications:Handling Relationships
Foreign Key Selection
UseuseSelect for dropdowns:
Search and Filter
Many-to-Many Relationships
Modal and Drawer Forms
Modal Form
Drawer Form
Multi-Step Forms
File Uploads
Single File Upload
Multiple Files
Best Practices
- Use proper validation - Validate on both client and server
- Handle loading states - Show spinners during save operations
- Implement auto-save carefully - Use appropriate debounce values
- Provide clear feedback - Show success/error notifications
- Handle unsaved changes - Warn users before leaving with unsaved data
- Use TypeScript - Type your form data for better DX
- Test edge cases - Handle network errors, validation errors, etc.
- Optimize re-renders - Use proper form state management
Next Steps
- Learn about Tables
- Explore Data Providers
- Discover Notifications