react-data-form

The form registry

addForm, getForm, and why the registry has to be a singleton.

Most forms are written where they are used and never need a name. Some have to be found by something that does not know them at compile time — a page builder offering block types, or a resource view looking up the form for the type it just fetched. That is what the registry is for.

What it is for#

  • The page builder. FormArrayInputController builds its palette by asking the registry for every form tagged with a given @for.
  • Resource views. react-resource-view stores resources in the very same registry, which is how a record's type resolves to the form that edits it.
  • Forms that arrive at runtime. A description fetched from an API can be registered on arrival and used like any other.

addForm and getForm#

blocks.ts
import { addForm, getForm } from "react-data-form"

addForm("block.hero", {
  name: "Hero",
  "@for": ["page-block"],
  inputs: {
    title: { label: "Title", required: true },
    subtitle: { label: "Subtitle" },
  },
})

// Later, anywhere in the application:
const hero = getForm({ type: "page-block" })
NameTypeDefaultDescription
addForm(name, form)(string, FormInterface & { "@for"?: string[] }) => FormInterfaceStores the form under name, stamping @id and @type: \"form\" on it.
getForm({ type })({ type: string }) => FormResourceItem | undefinedFinds a form whose @for contains type.
getForms(fors?)(string[]?) => FormResourceItem[]Every registered form, or those matching one of the given tags.
getFormType(form)(FormResourceItem) => stringThe tag to store on an item so getForm can find this form again — the first @for, falling back to the @id.
getFormLabel(form)(FormResourceItem) => stringA human-readable name for palettes and block headers.
upsertForm / updateFormutilitiesMerge a description into an existing one, for a form assembled from several sources.

@for is a tag, not a type

Several forms can share a tag: that is what makes a palette a list rather than a single entry. getForm returns one of them; getForms returns all of them.

Listing what is registered#

import { getForms, getFormLabel, getFormType } from "react-data-form"

// Every registered form
getForms()

// Only those tagged for the page builder
getForms(["page-block"]).map((form) => ({
  type: getFormType(form),   // the @for tag, or the @id
  label: getFormLabel(form), // name, or label.title, or the type
}))

One registry, or none at all#

addForm and createResource write into the same store, which lives in resource-registry as a module-level singleton.

// ✅ Both halves write into the same registry.
import { addForm } from "react-data-form"
import { createResource } from "resource-registry"

// ❌ Keeping a copy of createResource in your own code gives you a second
//    registry, and searchMetaData stops connecting a resource to its form.

Two copies is the failure mode to know

If resource-registry resolves twice in node_modules, you get two stores. Forms registered on one side become invisible from the other, and nothing errors — the palette is simply empty, and the lookup simply returns undefined.

That is why it is a peer dependency, and why you should import createResource from it rather than re-exporting a copy.