DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Creating multi-step forms in ReactJS
Jucian0
Jucian0

Posted on • Updated on

Creating multi-step forms in ReactJS

Hi folks, in this tutorial I want to show you how easily you can build a multi-step form using Createform library, and react-use-wizard. These forms are super useful when you want to save complicated forms or forms with many fields, so let's do that.

Introduction

One of the challenges of building multi-step forms is how to manage the entire form across many step components that should keep parts of the same form, with the same validation schema, same submit button, and same state. By using another library's forms, you should use React's Context API, and the useContext hook.

By contrast, when you use the Createform, you do not need anything like this. You need to create one form and use it across all steps. Let's get started and figure out how it is possible.

Setting up the Application

Our first task is to prepare an application to develop your example of a multi-step form. This can be done with React + Vite, and with Codesandbox it will be easier since there are templates for React + Vite on Codesandbox.

Next, we need to install some dependencies in order to develop your example, so we will install Createform, react-use-wizard, and to save some time and have a beautiful result, we can add chakra-ui. Feel free to use another library or CSS library.

yarn add @createform/react react-use-wizard
Enter fullscreen mode Exit fullscreen mode
yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
Enter fullscreen mode Exit fullscreen mode

Organizing the App Component

The first step is to organize the App.tsx file. We are about to add a title, and some stuff to make the layout look better.
In order to save time, we are already importing and add the PersonForm, this the component form that we are going to write in the next step.

import { ChakraProvider, Text, Box } from '@chakra-ui/react';
import { PersonForm } from './PersonForm/Form';

function App() {
  return (
    <ChakraProvider>
      <Box display="flex" justifyContent="center">
        <Text fontSize="2xl">Multi-step form example</Text>
      </Box>
      <PersonForm />
    </ChakraProvider>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Creating the form

Templates and components aren't the first things we need to think about, first, we need to think about what is really important, and what we are building. In order to build a multi-step form, we need to know how this form will work, if there are any rules or validations, how many fields it will have, and how many steps it will require before we develop the templates and components.

In this example, let's develop a form that submits information about a person. This form should have two steps:

  • The first step will deal with basic information about this person, like first name, last name, and age.

  • In the second step, we will deal with complementary information, such as city, street, and zip code.

Codding the usePersonForm Hook

To do that, we are going to create a file named usePersonForm.ts inside PersonForm directory:

import { createForm } from '@createform/react';

export const usePersonForm = createForm({
  initialValues: {
    firstName: '',
    lastName: '',
    age: null,
    address: {
      street: '',
      city: '',
      zipCode: '',
    },
  },
  ///mode: "onChange",
});
Enter fullscreen mode Exit fullscreen mode

Codding the Form component

The Form component should keep all the steps and handle the submit and reset events. We will import the Wizard component from react-use-wizard and our step components, BasicInfoStep and AddressStep, which will handle the logic for each step of the form.

import { usePersonForm } from './usePersonForm';
import { Wizard } from 'react-use-wizard';
import { BasicInfoStep } from './BasicInfoStep';
import { AddressStep } from './AddressStep';

export function PersonForm() {
  const form = usePersonForm();

  function handleSubmit(e) {
    console.log(e);
  }

  function handleReset(e) {}

  return (
    <form
      onReset={form.handleReset(handleReset)}
      onSubmit={form.handleSubmit(handleSubmit)}
    >
      <Wizard>
        <BasicInfoStep />
        <AddressStep />
      </Wizard>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Codding steps

The components of the steps should contain the form fields, and manage them.

Codding BasicInfoStep

  • The first thing we need to do is import, and use the useWizard hook. This hook manages the Wizard step position. It could be more convenient if we could manage it from Form, as it could save some lines of code. Unfortunately react-use-wizard doesn't allow it.

  • As a second step, import the usePersonForm hook, as we are about to use it here, to register each field with the register function.

  • The last step is to add some buttons to navigate to the next form step and reset the form value.

import { Input, Text } from '@chakra-ui/react';
import { usePersonForm } from './usePersonForm';
import { Button, Stack } from '@chakra-ui/react';
import { useWizard } from 'react-use-wizard';

export function BasicInfoStep() {
  const { previousStep, nextStep } = useWizard();
  const { register } = usePersonForm();

  return (
    <Stack p={10}>
      <Text fontWeight={'bold'}>Basic Info</Text>
      <Input mt={5} placeholder="First name" {...register('firstName')} />
      <Input mt={5} placeholder="Last name" {...register('lastName')} />
      <Input mt={5} placeholder="Age" type="number" {...register('age')} />

      <Stack direction="row" spacing={4} justify="center" mt={5}>
        <Button type="reset">Reset</Button>
        <Button onClick={nextStep}>Next</Button>
      </Stack>
    </Stack>
  );
}
Enter fullscreen mode Exit fullscreen mode

Codding AddressStep

  • Again, in the first, we need to import and use the useWizard hook, which manages the Wizard step position.

  • This step involves importing the usePersonForm hook, which will be used to register every field with the register function.

  • The last step is to add some buttons for submitting the form, resetting the form's value, and for navigating to the previous form step.

import { Input } from '@chakra-ui/react';
import { Button, Stack, Text } from '@chakra-ui/react';
import { usePersonForm } from './usePersonForm';
import { useWizard } from 'react-use-wizard';

export function AddressStep() {
  const { previousStep } = useWizard();
  const { register } = usePersonForm();

  return (
    <Stack p={10}>
      <Text fontWeight={'bold'}>Address</Text>
      <Input mt={5} placeholder="Street" {...register('address.street')} />
      <Input mt={5} placeholder="City" {...register('address.city')} />
      <Input mt={5} placeholder="Zip Code" {...register('address.zipCode')} />

      <Stack direction="row" spacing={4} justify="center" mt={5}>
        <Button onClick={previousStep}>Previous</Button>
        <Button type="reset">reset</Button>
        <Button type="submit">Submit</Button>
      </Stack>
    </Stack>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is the final file structure of the example:

You can see the result in the Codesandbox

Conclusion

Building a multi-step form can be easy if you use the right tools to build it. In this tutorial, I showed you how Createform can do that easily. Using Createform you can continue on, and create more complex forms, whether they are multi-step or not.

In the next post I'm going to show you, how to add some validation in this form.

Top comments (0)

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!