react-data-form

Introduction

What the library does, and what it deliberately does not.

react-data-form takes an object describing a form — its fields, their labels, what happens on submit — and renders it. It holds the state, runs the validators, and puts the violations your API returns back on the fields that caused them.

A first form#

Two imports and one object. useForm builds the form and owns its state; FormElement renders the whole thing — header, fields, errors and submit button.

ProfileForm.tsx
import { DatePickerInputController, FormElement, useForm } from "react-data-form"

function ProfileForm() {
  const formContext = useForm({
    form: {
      label: { title: "My profile" },
      inputs: {
        firstName: { label: "First name", required: true },
        email: { type: "email", label: "Email" },
        birthDate: { label: "Born on", controller: DatePickerInputController },
      },
      onSubmit: (data) => api.patch("/me", data),
    },
  })

  return <FormElement {...formContext} />
}

The form above, running

The payload beside the form is what onSubmit receives. That is the whole contract: a description goes in, a plain object comes out.

Why data, not JSX#

A form written as JSX can only be read by React. A form written as data can be stored in a database, merged with another one, filtered by permission, or generated from an OpenAPI schema — and only then handed to React.

// Nothing about this is JSX, so it can be stored, transformed,
// merged with another description, or generated from an API schema.
const form = {
  inputs: {
    title: { label: "Title", required: true },
    status: { label: "Status", controller: SelectInputController, valueOptions },
  },
}

This is also what lets react-resource-view build a table, an edit screen and a filter bar from the same object: the description is the single source, and every screen is a reading of it.

One field, one controller

Every field is rendered by a controller — a component receiving { formInput, onChange } and nothing else. Forty-odd ship with the package, and writing your own takes about ten lines.

What it does not do#

The library is deliberately narrow. It has no opinion on where your data comes from or where it goes:

  • No HTTP client. onSubmit receives an object; what you do with it is yours.
  • No schema library. A field's validator is a function that throws. Zod errors are understood, but nothing forces you to use Zod.
  • No router. A form is a component, not a page.
  • No visual identity. The components use Tailwind classes backed by the shadcn theme variables, so they take your palette rather than imposing one.

Where to go next#