Build dynamic React forms

Build advanced multi-step forms with just a JSON, easy as it has never been before.

app.js
package.json
const form = [
{ form: '...' },
{ cond: '...' },
{ loop: '...' },
{ variables: '...' },
// ...
]

How to use

Tutorial

Follow this tutorial to learn everything you need to know.


Overview

Creating a multi-step form like this in which there are questions dependent on previous answers can be a tedious and cumbersome task. Additionally, the challenges intensify when considering the need to store, share, or save such forms in a file or a database.

This is where Formity comes in, a solution designed to simplify the process. With Formity, building advanced multi-step forms is as straightforward as using a JSON. Say goodbye to complexities and experience form development like never before.

Dependencies

Formity depends on two packages, Mongu and React Hook Form. We suggest you to be at least a little familiar with these packages before moving on with this tutorial.

Example

To learn about Formity we highly suggest you to clone the following github repository.

git clone https://github.com/martiserra99/formity-example

This repository is set up with the Formity package. As we explain its usage, we'll be using the code within this repository as our point of reference.

cd formity-example
npm install
npm run dev

Installation

To install this package you have to run the following command.

npm install formity react-hook-form mongu

Components

To use this package the first thing you have to do is define the components that your form will use. You can find these at the components folder of the repository. They are created using Radix Themes, but you can create them in any way you want.

React Hook Form

There is one requirement when creating these components, though. If you take a look at the form fields like the TextField component, you will see that they need to be registered to the form, as this package depends on React Hook Form.

export default function TextField({ label, name, placeholder }) {
  const { register, formState } = useFormContext()
  const error = formState.errors[name]
  return (
    <Text as="label" className={styles.label}>
      <Label as="div" mb="1" error={error}>
        {label}
      </Label>
      <RadixTextField.Input
        autoComplete="off"
        placeholder={placeholder}
        {...register(name)}
        {...(error && { color: 'red' })}
      />
      {error && <ErrorMessage mt="1">{error.message}</ErrorMessage>}
    </Text>
  )
}

FormityProvider

After defininig these components you have to provide them using the FormityProvider component. You can find how this component is used in the main.jsx file.

import { FormityProvider } from 'formity'

// ...

const components = {
  LayoutForm,
  TextField,
  TextArea,
  Select,
  RadioGroup,
  CheckboxGroup,
  Slider,
  Range,
  Button,
  Back,
}

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Theme appearance="dark" panelBackground="translucent">
      <FormityProvider components={components}>
        <App />
      </FormityProvider>
    </Theme>
  </React.StrictMode>,
)

Formity

Once you have the components defined you can start to build the form. If you take a look at App.jsx, you will see that the form is created using the Formity component.

import { Formity } from 'formity'

// ...

import form from './form'

export default function App() {
  const [result, setResult] = useState(null)

  function handleSubmit(result) {
    setResult(result)
  }

  // ...

  return (
    <Center>
      <Card>
        <Formity form={form} onSubmit={handleSubmit} />
      </Card>
    </Center>
  )
}

This component receives two props, the first one is the JSON form and the second one is the callback that will be called when the form is submitted.

JSON

The JSON that defines the form is in form.json. As you inspect it, you'll notice that this JSON is defined as an array of elements of different types. All these elements used in combination let you create any form you want in a very simple way.

The type of elements that exist are Form, Return, Variables, Condition and Loop. These elements let you create all kinds of logic and you can find more about them in the next part of the documentation.

Back

Finally, to navigate back to previous steps while using the form, you can use the useFormityForm() hook. You can check out how it is used in the Back component.

import { useFormityForm } from 'formity'

import { Button } from '@radix-ui/themes'

export default function Back({ children }) {
  const { onBack } = useFormityForm()
  return (
    <Button type="button" variant="outline" onClick={onBack}>
      {children}
    </Button>
  )
}

License

MIT License

Next
Form