Skip to main content
Version: 2.xx.xx


useForm is used to manage forms. It uses Ant Design Form data scope management under the hood and returns the required props for managing the form actions.


We'll show the basic usage of useForm by adding an editing form.

import { Edit, Form, Input, useForm, Select } from "@pankod/refine";

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps } = useForm<IPost>();

return (
<Edit saveButtonProps={saveButtonProps}>
<Form {...formProps} layout="vertical">
<Form.Item label="Title" name="title">
<Input />
<Form.Item label="Status" name="status">
label: "Published",
value: "published",
label: "Draft",
value: "draft",
label: "Rejected",
value: "rejected",

interface IPost {
id: string;
title: string;
status: "published" | "draft" | "rejected";
const { formProps, saveButtonProps } = useForm<IPost>();

formProps includes all necessary values to manage Ant Design Form components.

In the example if you navigate to /posts/edit/1234 it will manage the data of the post with id of 1234 in an editing context. See Actions on how useForm determines this is an editing context.

Since this is an edit form it will fill the form with the data of the post with id of 1234 and then the form will be ready to edit further and submit the changes.

Submit functionality is provided by saveButtonProps which includes all of the necessary props for a button to submit a form including automatically updating loading states.

useForm accepts type parameters for the record in use and for the response type of the mutation. IPost in the example represents the record to edit. It is also used as the default type for mutation response.


useForm can handle edit, create and clone actions.


By default it determines the action from route. In the example, the route is /posts/edit/1234 thus this is an editing form.

It can take an action parameter for the situations where it isn't possible to determine the action from route i.e. using a form in a modal, using a custom route.

const { formProps, saveButtonProps } = useForm({ action: "edit" });

action: "edit"

action: "edit" is used for editing an existing record. Form will initially be filled with the data of the record.

useForm uses useUpdate under the hood for mutations on edit mode.

action: "create"

action: "create"is used for creating a new record that didn't exist before.

useForm uses useCreate under the hood for mutations on create mode.

Clone mode

When creating a new record, useForm can initialize the form with the data of an existing record.

useForm works on clone mode when a route has a clone and id parameters like this {{resourceName}}/clone/1234. Alternatively, if route doesn't have those parameters, action can be set with action: "clone" and id can be set with setCloneId.

const { setCloneId } = useForm();

If you want to show a form in a modal or drawer where necessary route params might not be there you can use the useModalForm or the useDrawerForm hook.


<CloneButton> can be used to navigate to a clone route with an id like this {{resourceName}}/clone/1234.

<CloneButton recordItemId={} />

Also the clone method from the useNavigation hook can be used as well.

const { clone } = useNavigation();

<Button onClick={() => clone("posts",} />

API Reference


actionType of the form mode"edit" | "create"
resourceResource name for API data interactionsstring
onMutationSuccessCalled when a mutation is successful(data: UpdateResponse<M>, variables: any, context: any) => void
onMutationErrorCalled when a mutation encounters an error(error: any, variables: any, context: any) => void
mutationModeDetermines when mutations are executed "pessimistic | "optimistic | "undoable""pessimistic"*
submitOnEnterListens Enter key press to submit formbooleanfalse
warnWhenUnsavedChangesShows notification when unsaved changes existbooleanfalse*
redirectPage to redirect after a succesfull mutation "show | "edit | "list" | false"list"
undoableTimeoutDuration to wait before executing mutations when mutationMode = "undoable"number5000*
successNotificationSuccessful Mutation notificationSuccessErrorNotification"Successfully created resource" or "Successfully updated resource"
errorNotificationUnsuccessful Mutation notificationSuccessErrorNotification"There was an error creating resource (status code: statusCode)" or "Error when updating resource (status code: statusCode)"
metaDataMetadata query for dataProviderMetaDataQuery{}
liveModeWhether to update data automatically ("auto") or not ("manual") if a related live event is received. The "off" value is used to avoid creating a subscription."auto" | "manual" | "off""off"
liveParamsParams to pass to liveProvider's subscribe method if liveMode is enabled.{ ids?: string[]; [key: string]: any; }undefined
onLiveEventCallback to handle all related live events of this hook.(event: LiveEvent) => voidundefined

*: These props have default values in RefineContext and can also be set on <Refine> component. useForm will use what is passed to <Refine> as default but a local value will override it.

Return values

formAnt Design form instanceFormInstance
formPropsAnt Design form propsFormProps
saveButtonPropsProps for a submit button{ disabled: boolean; onClick: () => void; loading?:boolean; }
queryResultResult of the query of a recordQueryObserverResult<T>
mutationResultResult of the mutation triggered by submitting the formUseMutationResult<T>
formLoadingLoading state of form requestboolean
cloneIdRecord id for clone action"string" | "number"
setCloneIdcloneId setterDispatch<SetStateAction< string | number | undefined>>
editIdRecord id for edit action"string" | "number"
setEditIdeditId setterDispatch<SetStateAction< string | number | undefined>>

Type Parameters

TDataResult data of the query that extends BaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpError
TVariablesValues for params.{}

Live Codesandbox Example