DEV Community

Lee Twito
Lee Twito

Posted on

Building an Advanced Multistep Form with React-Hook-Form

Building an Advanced Multistep Form with React Hook Form

In this blog post, we'll demonstrate how to create an advanced multistep form using React Hook Form, a popular form library for React. React Hook Form aims to minimize re-rendering and provides a better developer experience by leveraging hooks and the native HTML form behavior.

Creating the Form Context

To manage our form data, we'll create a custom form context. This context will store the form data and provide a function to update it.

// src/FormContext.js
import { createContext, useContext, useState } from "react";

const FormContext = createContext();

export const useFormContext = () => {
  return useContext(FormContext);
};

export const FormProvider = ({ children }) => {
  const [formData, setFormData] = useState({});

  const updateFormData = (updatedData) => {
    setFormData((prevData) => ({ ...prevData, ...updatedData }));
  };

  return (
    <FormContext.Provider value={{ formData, updateFormData }}>
      {children}
    </FormContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Creating the Form Steps

Now, let's create three form steps: Personal Info, Employment, and Review.

Personal Info Step

// src/steps/PersonalInfo.js
import { useForm } from "react-hook-form";
import { useHistory } from "react-router-dom";
import { useFormContext } from "../FormContext";

export const PersonalInfo = () => {
  const { register, handleSubmit } = useForm();
  const { updateFormData } = useFormContext();
  const history = useHistory();

  const onSubmit = (data) => {
    updateFormData(data);
    history.push("/employment");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="name">Name</label>
        <input {...register("name")} id="name" required />
      </div>
      <div>
        <label htmlFor="email">Email</label>
        <input {...register("email")} id="email" required />
      </div>
      <button type="submit">Next</button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

Employment Step

// src/steps/Employment.js
import { useForm } from "react-hook-form";
import { useHistory } from "react-router-dom";
import { useFormContext } from "../FormContext";

export const Employment = () => {
  const { register, handleSubmit } = useForm();
  const { updateFormData } = useFormContext();
  const history = useHistory();

  const onSubmit = (data) => {
    updateFormData(data);
    history.push("/review");
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label htmlFor="company">Company</label>
        <input {...register("company")} id="company" />
      </div>
      <div>
        <label htmlFor="position">Position</label>
        <input {...register("position")} id="position" />
      </div>
      <button type="submit">Next</button>
    </form>
  );
};
Enter fullscreen mode Exit fullscreen mode

Review Step

// src/steps/Review.js
import { useFormContext } from "../FormContext";

export const Review = () => {
  const { formData } = useFormContext();

  return (
    <div>
      <h2>Review</h2>
      <pre>{JSON.stringify(formData, null, 2)}</pre>
      <p>Once you're satisfied with the data, you can submit the form to a backend service or save it to a local storage.</p>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Setting Up Routes

Now, let's create a Router component to handle navigation between the steps:

// src/Router.js
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { PersonalInfo } from "./steps/PersonalInfo";
import { Employment } from "./steps/Employment";
import { Review } from "./steps/Review";

export const Router = () => {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/" component={PersonalInfo} />
        <Route path="/employment" component={Employment} />
        <Route path="/review" component={Review} />
      </Switch>
    </BrowserRouter>
  );
};
Enter fullscreen mode Exit fullscreen mode

Updating App.js

Finally, wrap the Router component in App.js with the FormProvider:

// src/App.js
import { FormProvider } from "./FormContext";
import { Router } from "./Router";

function App() {
  return (
    <div className="App">
      <FormProvider>
        <Router />
      </FormProvider>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Conclusion

In this blog post, we've built an advanced multistep form using React Hook Form and React Router. We've also created a custom form context to manage form data throughout the steps. By following this approach, you can create complex multistep forms with ease, while leveraging the benefits of React Hook Form.


As a potential enhancement, you could integrate with third-party libraries like Tutim for schema-based implementation including validation, form persistence, conditional rendering, save draft, and more.

Top comments (0)