Docs / SDK / Fields & AutoForm

Fields & AutoForm

The field system renders any data type -- strings, numbers, colors, vectors, entity references -- as density-aware form controls. AutoForm generates entire forms from Zod schemas automatically.

FieldRenderer

FieldRenderer is the master field dispatcher. Give it a field definition and a value, and it resolves the correct component from the registry:

import { FieldRenderer } from "@substrateui/ui/fields";

<FieldRenderer
  definition={{ path: "name", type: "string", meta: { label: "Name" } }}
  value={contact.name}
  onChange={(val) => update("name", val)}
/>

<FieldRenderer
  definition={{ path: "color", type: "color", meta: { label: "Color" } }}
  value="#3B82F6"
  onChange={(val) => update("color", val)}
/>

<FieldRenderer
  definition={{ path: "position", type: "vector3", meta: { label: "Position" } }}
  value={{ x: 0, y: 10, z: 5 }}
  onChange={(val) => update("position", val)}
/>

Built-in field adapters

The field registry ships with adapters for all common types. Each adapter automatically responds to the current density mode:

TypeComponentDescription
stringStringFieldText input
numberNumberFieldNumeric input with step controls
booleanBooleanFieldSwitch / toggle
textareaTextareaFieldMulti-line text
range / sliderSliderFieldRange slider
enum / selectSelectFieldDropdown, combobox, or multi-select
colorColorFieldColor picker with swatch
vector2Vector2Field2D vector (x, y)
vector3Vector3Field3D vector (x, y, z)
codeCodeFieldCode editor
ref / relationEntityRefFieldEntity reference picker
tagsTagsFieldTag input

Custom field adapters

Register your own field types using the component registry. Your adapter receives FieldAdapterProps<T> with value, onChange, meta, path, and validation state:

import { componentRegistry } from "@substrateui/ui/core";
import type { FieldAdapterProps } from "@substrateui/ui/core";

function CurrencyField({ value, onChange, meta, disabled }: FieldAdapterProps<number>) {
  return (
    <div className="flex items-center gap-1">
      <span className="text-muted-foreground">$</span>
      <input
        type="number"
        value={value ?? 0}
        onChange={(e) => onChange(parseFloat(e.target.value))}
        disabled={disabled}
        step={0.01}
        className="..."
      />
    </div>
  );
}

// Register for the "currency" field type
componentRegistry.registerFieldType("currency", CurrencyField);

AutoForm

AutoForm generates a complete form from a Zod schema. It extracts field definitions, resolves components from the registry, and handles validation:

import { z } from "zod";
import { AutoForm } from "@substrateui/ui/fields";

const contactSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.email("Invalid email"),
  phone: z.string().optional(),
  company: z.string().optional(),
  role: z.enum(["admin", "member", "viewer"]),
  active: z.boolean().default(true),
  notes: z.string().optional(),
});

export function ContactForm() {
  return (
    <AutoForm
      schema={contactSchema}
      defaultValues={{ active: true }}
      onSubmit={(values) => console.log(values)}
      columns={2}
      groups={[
        { id: "info", label: "Contact Info", fields: ["name", "email", "phone"] },
        { id: "org", label: "Organization", fields: ["company", "role"] },
        { id: "meta", label: "Meta", fields: ["active", "notes"] },
      ]}
      footer={
        <button type="submit" className="...">Save Contact</button>
      }
    />
  );
}

AutoForm props

PropTypeDescription
schemaz.ZodObjectZod schema to generate the form from
valuesRecord<string, unknown>Controlled values (optional)
defaultValuesRecord<string, unknown>Initial values
onChange(path, value) => voidCalled on every field change
onSubmit(values) => voidCalled on form submission
columns1 | 2 | 3 | 4Number of form columns
groupsGroup[]Group fields into labeled sections
errorsRecord<string, string>External validation errors
disabledbooleanDisable all fields
readOnlybooleanMake all fields read-only
headerReactNodeContent above the form
footerReactNodeContent below (usually a submit button)

Density integration

All field adapters automatically respond to the current density. Wrap your form in a DensityProvider (or use SubstrateRenderer) and field heights, font sizes, and spacing adjust automatically across compact, comfortable, and spacious modes.