Skip to content

React ​

The React adapter renders definitions with Formisch and your own components. It reads the definition, connects every field to Formisch and turns input into the values the schema expects, but renders nothing of its own. Below you will learn how to set it up.

Installation ​

sh
npm install @whatsaaaa/form-builder-react @whatsaaaa/form-builder-schema @formisch/react valibot
sh
pnpm add @whatsaaaa/form-builder-react @whatsaaaa/form-builder-schema @formisch/react valibot
sh
yarn add @whatsaaaa/form-builder-react @whatsaaaa/form-builder-schema @formisch/react valibot
sh
bun add @whatsaaaa/form-builder-react @whatsaaaa/form-builder-schema @formisch/react valibot

React 18 or 19, @formisch/react 1.2 and Valibot 1.4.1 or later are required. @whatsaaaa/form-builder-schema is a peer dependency, so install it too: the adapter and your code share its one copy, with the createSchemaBuilder and types your code imports.

Usage ​

useJsonForm builds the schema and the initial input, and creates the Formisch form. <JsonFields> renders its fields inside Formisch's <Form>, each with your component for its type:

tsx
import { Form } from '@formisch/react'
import type { JsonForm } from '@whatsaaaa/form-builder-schema'
import { JsonFields, useJsonForm } from '@whatsaaaa/form-builder-react'
import { components } from './fields'

export function SignUpForm({ definition }: { definition: JsonForm }) {
  const jsonForm = useJsonForm(definition, { components })

  function save(output: unknown) {
    // { email: 'jane@example.com', password: 'correct horse', newsletter: true }
    console.log(output)
  }

  return (
    <Form of={jsonForm.form} onSubmit={save}>
      <JsonFields of={jsonForm} />
      <button type="submit">Sign up</button>
    </Form>
  )
}
  • jsonForm.form is a normal Formisch form, so every Formisch method works on it, like reset and setInput.
  • The form element, the submit button and anything around them are yours.
  • A definition with issues throws the builder's DefinitionError while rendering, so an error boundary can catch it. Run validate() first when the definition comes from outside your code.
  • Like Formisch's useForm, useJsonForm reads the definition on the first render. To show another one, give the component that calls it a new key: <SignUpForm key={id} definition={definition} />.

Options ​

useJsonForm takes the builder, your components, and Formisch's validation modes:

ts
// Built once, outside the component: the builder is read on the first render.
const builder = createSchemaBuilder({ extensions: [affects()] })

export function useOrderForm(definition: JsonForm) {
  const jsonForm = useJsonForm(definition, {
    // Your own types, rules, messages and extensions. Defaults to createSchemaBuilder().
    builder,
    // Your component for each field type.
    components,
    // Your component for each layout type.
    layoutComponents,
    // When a field is validated the first time, and again after that, as in Formisch.
    validate: 'blur',
    revalidate: 'input',
  })
  return jsonForm
}

builder, validate and revalidate are read on the first render, with the definition, so make the builder once, outside the component. components and layoutComponents are read on every render. They can also go to <JsonFields> and <JsonField> as props, which win over the options.

Pass a builder when your definitions use your own types, rules or messages, or the conditions of the affects package.

Not included ​

  • Labels and display text reach your components as written in the definition. The builder's resolveMessage only changes what goes into the schema, so translate field.label in your components.
  • ui.widget doesn't pick a component. A component can read field.ui itself, like a multiselect that shows checkboxes.

Next steps ​

Continue to field components to write a component for every field type.