20 January 2025

Formity: The Ultimate Tool For Multi-Step Forms In React

As a frontend developer, you've likely faced the challenge of creating multi-step forms. These forms can be tricky to build, especially when they must adapt dynamically to user responses. Formity is designed to simplify this process, filling the gap left by existing form libraries.

What is your name?

The Problem With Existing Form Libraries

There are many great form libraries available, such as React Hook Form, Formik, and Tanstack Form, which excel at handling single-step forms efficiently. However, these form libraries lack built-in solutions for managing multi-step navigation and advanced logic. For instance, building an onboarding process or an interactive survey can be quite challenging with these tools.

This is where Formity steps in - a library to address the complexities of multi-step forms, offering developers the tools they need to create forms that are both dynamic and scalable.

Key Features of Formity

Formity provides a range of features that make it the go-to tool for creating advanced forms.

Advanced Logic

You can add sophisticated logic to your multi-step forms using features such as conditions, loops, and variables. This enables you to create truly dynamic forms where the flow adapts to user input.

Integration With Any Form Library

Formity doesn't force you to abandon your favorite form library. You can seamlessly integrate it with libraries like React Hook Form, Formik, or any other form-handling tool.

Advanced Type Inference

For TypeScript users, Formity offers advanced type inference to make the developer experience smooth and error-free. You'll get better autocomplete and improved type safety.

Type inference

How It Works

Formity's simplicity lies in its intuitive API. To create a multi-step form, you only need to:

  • Use the Formity component.

  • Create a schema that specifies the structure and behavior of the form.

Formity component

The Formity component is used to render the multi-step form.

import { useCallback, useState } from "react";

import { Formity, type OnReturn, type ReturnOutput } from "@formity/react";

import { Output } from "./components/output";

import { schema, type Values } from "./schema";

export default function App() {
  const [output, setOutput] = useState<ReturnOutput<Values> | null>(null);

  const onReturn = useCallback<OnReturn<Values>>((output) => {
    setOutput(output);
  }, []);

  if (output) {
    return <Output output={output} onStart={() => setOutput(null)} />;
  }

  return <Formity<Values> schema={schema} onReturn={onReturn} />;
}

Form schema

The form schema is what defines the structure and behavior of the form.

// ...

export const schema: Schema<Values> = [
  {
    form: {
      values: () => ({
        working: ["yes", []],
      }),
      render: ({ values, ...props }) => (
        <FormStep
          id="working"
          defaultValues={values}
          resolver={zodResolver(
            z.object({
              working: z.string(),
            }),
          )}
          question="Are you working?"
          answer={{
            type: "dropdown",
            name: "working",
            options: [
              { value: "yes", label: "Yes" },
              { value: "no", label: "No" },
            ],
          }}
          nextButton="Ok"
          backButton={null}
          {...props}
        />
      ),
    },
  },
  {
    cond: {
      if: ({ working }) => working === "yes",
      then: [
        {
          form: {
            values: () => ({
              company: ["", []],
            }),
            render: ({ values, ...props }) => (
              <FormStep
                id="company"
                defaultValues={values}
                resolver={zodResolver(
                  z.object({
                    company: z.string().min(1, "Required"),
                  }),
                )}
                question="At what company?"
                answer={{
                  type: "shortText",
                  name: "company",
                  placeholder: "Company name",
                }}
                nextButton="Ok"
                backButton="Go Back"
                {...props}
              />
            ),
          },
        },
        {
          return: ({ company }) => ({
            working: true,
            company,
          }),
        },
      ],
      else: [
        {
          form: {
            values: () => ({
              searching: ["yes", []],
            }),
            render: ({ values, ...props }) => (
              <FormStep
                id="searching"
                defaultValues={values}
                resolver={zodResolver(
                  z.object({
                    searching: z.string().min(1, "Required"),
                  }),
                )}
                question="Are you looking for a job?"
                answer={{
                  type: "dropdown",
                  name: "searching",
                  options: [
                    { value: "yes", label: "Yes" },
                    { value: "no", label: "No" },
                  ],
                }}
                nextButton="Ok"
                backButton="Go Back"
                {...props}
              />
            ),
          },
        },
        {
          return: ({ searching }) => ({
            working: false,
            searching,
          }),
        },
      ],
    },
  },
];

Real-World Use Cases

Formity provides the flexibility to create applications that require dynamic, adaptive logic, making it ideal for handling complex workflows. Below we provide two practical examples.

Example #1: Quiz application

A quiz application that asks the user several questions and returns the number of correct answers.

What is the capital of Australia?

Example #2: Contact form

A contact form for a web development agency specializing in tailored digital solutions for each client.

What do you need help with?

Why Choose Formity?

Formity excels at solving the complex challenges of multi-step forms. It provides developers with a powerful toolkit to design scalable, dynamic, and logic-driven solutions for a wide range of use cases, ensuring flexibility and ease of use.

So next time you're tasked with creating a multi-step form, give Formity a try. With its advanced logic capabilities, compatibility with popular form libraries, and robust TypeScript support, it empowers frontend developers to build dynamic and adaptive forms effortlessly.