Skip to main content
Version: 3.xx.xx

useStepsForm

useStepsForm hook allows you to split your form under an Ant Design based Steps component and provides you with a few useful functionalities that will help you manage your form.

import { useStepsForm } from "@pankod/refine-antd";

const { stepsProps, formProps } = useStepsForm<IPost>();

All we have to do is to pass the props it returns to our <Steps> and <Form> components.

Usage​

We'll do two examples, one for creating and one for editing a post. Let's see how useStepsForm is used in both.

Create​

For the sake of simplicity, in this example we're going to build a Post create form that consists of only a title and a relational category field.

To split your form items under a <Steps> component, first import and use useStepsForm hook in your page:

pages/posts/create.tsx
import { useStepsForm } from "@pankod/refine-antd";

export const PostCreate: React.FC = () => {
const {
current,
gotoStep,
stepsProps,
formProps,
saveButtonProps,
queryResult,
} = useStepsForm<IPost>();

return null;
};

interface ICategory {
id: number;
title: string;
}

interface IPost {
id: number;
title: string;
status: "published" | "draft" | "rejected";
}

useStepsForm is generic over the type form data to help you type check your code.

This hook returns a set of useful values to render steps form. Given current value, you should have a way to render your form items conditionally with this index value. You can use an array to achieve this.

Here, each item of formList corresponds to one step in form:

pages/posts/create.tsx
import { useStepsForm, useSelect, Form, Input, Select } from "@pankod/refine-antd";

export const PostCreate: React.FC = () => {
const {
current,
gotoStep,
stepsProps,
formProps,
saveButtonProps,
} = useStepsForm<IPost>();

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
});

const formList = [
<>
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</>,
<>
<Form.Item
label="Category"
name={["category", "id"]}
rules={[
{
required: true,
},
]}
>
<Select {...categorySelectProps} />
</Form.Item>
</>,
];

return null;
};
tip

Since category is a relational data, we use useSelect to fetch its data.

Refer to useSelect documentation for detailed usage. β†’


You should use stepsProps on <Steps> component, formProps on the <Form> component respectively. And as the last step, you should render the <Steps> component besides the form like this:

pages/posts/create.tsx
import {
useStepsForm,
useSelect,
Form,
Input,
Select,
Create,
Steps,
} from "@pankod/refine-antd";

export const PostCreate: React.FC = () => {
const {
current,
gotoStep,
stepsProps,
formProps,
saveButtonProps,
queryResult,
} = useStepsForm<IPost>();

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
});

const formList = [
<>
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</>,
<>
<Form.Item
label="Category"
name={["category", "id"]}
rules={[
{
required: true,
},
]}
>
<Select {...categorySelectProps} />
</Form.Item>
</>,
];

return (
<Create saveButtonProps={saveButtonProps}>
<Steps {...stepsProps}>
<Steps.Step title="First Step" />
<Steps.Step title="Second Step" />
</Steps>
<Form {...formProps} layout="vertical">
{formList[current]}
</Form>
</Create>
);
};
Important

Make sure to add as much <Steps.Step> components as the number of steps in the formList array.


To help users navigate between steps in the form, you can use action buttons. Your navigation buttons should use the gotoStep function that was previously returned from the the useStepsForm hook.

pages/posts/create.tsx
import {
useStepsForm,
useSelect,
Form,
Input,
Select,
Create,
Steps,
Button,
SaveButton,
} from "@pankod/refine-antd";

export const PostCreate: React.FC = () => {
const {
current,
gotoStep,
stepsProps,
formProps,
saveButtonProps,
queryResult,
submit,
} = useStepsForm<IPost>();

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
});

const formList = [
<>
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</>,
<>
<Form.Item
label="Category"
name={["category", "id"]}
rules={[
{
required: true,
},
]}
>
<Select {...categorySelectProps} />
</Form.Item>
</>,
];

return (
<Create
actionButtons={
<>
{current > 0 && (
<Button
onClick={() => {
gotoStep(current - 1);
}}
>
Previous
</Button>
)}
{current < formList.length - 1 && (
<Button
onClick={() => {
gotoStep(current + 1);
}}
>
Next
</Button>
)}
{current === formList.length - 1 && (
<SaveButton
{...saveButtonProps}
style={{ marginRight: 10 }}
onClick={() => submit()}
/>
)}
</>
}
>
<Steps {...stepsProps}>
<Steps.Step title="First Step" />
<Steps.Step title="Second Step" />
</Steps>
<Form {...formProps} layout="vertical">
{formList[current]}
</Form>
</Create>
);
};
Steps form example

Edit​

In this example, we'll just look at what's different from the example above.

pages/posts/edit.tsx
import {
useStepsForm,
useSelect,
Form,
Input,
Select,
Steps,
Button,
SaveButton,
Edit,
} from "@pankod/refine-antd";

export const PostCreate: React.FC = () => {
const {
current,
gotoStep,
stepsProps,
formProps,
saveButtonProps,
queryResult,
submit,
} = useStepsForm<IPost>();

const postData = queryResult?.data?.data;
const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
defaultValue: postData?.category.id,
});

const formList = [
<>
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
</>,
<>
<Form.Item
label="Category"
name={["category", "id"]}
rules={[
{
required: true,
},
]}
>
<Select {...categorySelectProps} />
</Form.Item>
</>,
];

return (
<Edit
actionButtons={
<>
{current > 0 && (
<Button
onClick={() => {
gotoStep(current - 1);
}}
>
Previous
</Button>
)}
{current < formList.length - 1 && (
<Button
onClick={() => {
gotoStep(current + 1);
}}
>
Next
</Button>
)}
{current === formList.length - 1 && (
<SaveButton
{...saveButtonProps}
style={{ marginRight: 10 }}
onClick={() => submit()}
/>
)}
</>
}
>
<Steps {...stepsProps}>
<Steps.Step title="First Step" />
<Steps.Step title="Second Step" />
</Steps>
<Form {...formProps} layout="vertical">
{formList[current]}
</Form>
</Edit>
);
};

API Reference​

Properties​

KeyDescriptionTypeDefault
action
Required
Type of form mode"edit" | "create"| "clone""create"
idRecord id for fetchingBaseKeyId that it reads from the URL
defaultCurrentDefault step, counting from 0number0
totalTotal count of steps stepsnumber0
isBackValidateShould validate if went to the previous step stepbooleanfalse
formAnt Design form instanceFormInstance<TVariables>
mutationModeDetermines when mutations are executed. If not explicitly configured, it is read from the mutation mode config of the resource in current route"pessimistic" | "optimistic" | "undoable"
onMutationErrorCalled when a mutation encounters an error(error: any, variables: any, context: any) => void
onMutationSuccessCalled when a mutation is successful(data: UpdateResponse<M>, variables: any, context: any) => void
redirectPage to redirect after a succesfull mutation "show" | "edit" | "list" | "create" | false"list"
submitSubmit the form(values?: TVariables) => Promise<TData>
submitOnEnterListen Enter key press to submit formbooleanfalse
undoableTimeoutDuration to wait before executing mutations when mutationMode = "undoable"number5000*
warnWhenUnsavedChangesShows notification when unsaved changes existbooleanfalse*
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?: BaseKey[]; [key: string]: any; }undefined
onLiveEventCallback to handle all related live events of this hook.(event: LiveEvent) => voidundefined
invalidatesYou can use it to manage the invalidations that will occur at the end of the mutation.all, resourceAll, list, many, detail, false["list", "many", "detail"]
queryOptionsreact-query queryOptions of useOne hook used while in edit mode. UseQueryOptions<
{ data: TData[]; },
TError>

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

**: If not explicitly configured, default value of redirect depends on which action used. If action is create, redirects default value is edit (created resources edit page). if action is edit instead, redirects default value is list.

Return Values​

KeyDescriptionType
stepsPropsAnt Design steps propsStepsProps
currentCurrent step, counting from 0.number
gotoStepGo to the target step(step: number) => void
formPropsAnt Design form propsFormProps
formAnt Design form instanceFormInstance<TVariables>
formLoadingLoading status of formboolean
defaultFormValuesLoadingDefaultFormValues loading status of formboolean
submitSubmit method, the parameter is the value of the form fields() => void
saveButtonPropsProps for a submit button{ disabled: boolean; onClick: () => void; loading: boolean; }
queryResultResult of the query of a recordQueryObserverResult<{ data: TData }>
mutationResultResult of the mutation triggered by submitting the formUseMutationResult<
{ data: TData },
TError,
{ resource: string; values: TVariables; },
unknown>](https://react-query.tanstack.com/reference/useMutation)
idRecord id for clone and create actionBaseKey
setIdid setterDispatch<SetStateAction< string | number | undefined>>

Type Parameters​

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

Live StackBlitz Example​