react-data-form

Installation

Install, wire Tailwind up, and understand why two dependencies are peers.

Install#

pnpm add react-data-form react-mini-i18n resource-registry

react and react-dom — 18.3 or 19 — react-mini-i18n and resource-registry are peer dependencies: your copies are the ones used.

Styles#

The components are written with Tailwind CSS v4 classes backed by the shadcn theme variables. Two things have to be true for them to look like anything.

1. Tailwind has to see the library

app.css
@import "tailwindcss";

/* Tailwind only generates the classes it can see, and the library's classes
   live in its compiled files. */
@source "../node_modules/react-data-form/dist";

Without the @source line the markup renders with class names Tailwind never generated, and the form comes out unstyled.

2. The theme variables have to exist

If your application already has a shadcn theme, do not import the library's stylesheet — your variables are enough, and the components pick your palette up automatically. That is what this site does.

/* Only if your application has no shadcn theme of its own. */
@import "react-data-form/styles.css";

Otherwise, define the variables yourself and the library follows along:

:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.52 0.19 273);
  --primary-foreground: oklch(0.99 0 0);
  --muted: oklch(0.97 0 0);
  --muted-foreground: oklch(0.556 0 0);
  --border: oklch(0.891 0 0);
  --input: oklch(0.97 0 0);
  --ring: oklch(0.457 0 0);
  --radius: 0.75rem;
  /* …and the rest of the shadcn set. */
}

Why two peer dependencies#

react-mini-i18n and resource-registry each own a module-level singleton: a translation dictionary on one side, a resource registry on the other.

Two copies means two registries

If both your application and the library resolved their own copy in node_modules, you would end up with two dictionaries and two registries. Half your translations would look ignored, and a form registered on one side would be invisible from the other.

Declaring them as peers is what forces a single copy. It also means you should import createResource from resource-registry directly rather than keeping a copy of it in your own code.

Entry points#

ImportContents
react-data-formCore: useForm, FormElement, every field controller, configuration
react-data-form/groupSplitting fields into collapsible sections
react-data-form/mediaImage editor: cropping and rotation
react-data-form/stepMulti-step forms with navigation
react-data-form/styles.cssThe optional neutral theme

The split is about weight, not taste: the media editor pulls in a cropping library and the WYSIWYG field pulls in an editor, so neither belongs in the entry point every application imports.

Check it works#

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

export function SmokeTest() {
  const formContext = useForm({
    form: { inputs: { hello: { label: "Hello" } } },
  })

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

You should see a labelled input and a submit button. If instead you see:

  • unstyled markup — the @source line is missing or points at the wrong path;
  • black on black — the theme variables are not defined;
  • nothing at allFormElement was given the form rather than the context; it takes what useForm returns, spread.