ErlangFormDocs v1.0.0
Structured data

Unions and discriminated unions

Render branches using a discriminator and understand union limitations.

Discriminated unions

A discriminated union chooses the active object branch from a discriminator field. The renderer infers its choices from every branch. Configure fields from all branches under the union parent; callbacks can narrow the object using its discriminator.

Usage

Use your form instance from useForm. See the initial setup.

example.tsx
const schema = z.object({
  contact: z.discriminatedUnion("kind", [
    z.object({ kind: z.literal("person"), name: z.string() }),
    z.object({ kind: z.literal("company"), company: z.string() }),
  ]),
});

<ErlForm
  form={form}
  schema={schema}
  fieldConfig={{
    contact: {
      name: { label: "Personal name" },
      company: { label: "Company name" },
    },
  }}
/>

Use a single string literal discriminator per branch and initialize a valid branch. If the current discriminator matches none of them, the renderer shows the first branch as a fallback; that does not make the data valid.

Changing branches does not automatically populate every new field or remove values from the previous branch. Normalize state in your application when needed and validate before persistence.

Plain unions and intersections

A plain union whose variants infer to the same metadata type uses that renderer; string literal options are combined. A mixed union such as z.union([z.string(), z.number()]) becomes unknown and has no built-in input. Use the unknown registry entry or a discriminated union.

An intersection of objects combines both child metadata lists without deduplicating names. Avoid duplicate fields or explicitly merge the object schemas.

On this page