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:
| Type | Component | Description |
|---|---|---|
| string | StringField | Text input |
| number | NumberField | Numeric input with step controls |
| boolean | BooleanField | Switch / toggle |
| textarea | TextareaField | Multi-line text |
| range / slider | SliderField | Range slider |
| enum / select | SelectField | Dropdown, combobox, or multi-select |
| color | ColorField | Color picker with swatch |
| vector2 | Vector2Field | 2D vector (x, y) |
| vector3 | Vector3Field | 3D vector (x, y, z) |
| code | CodeField | Code editor |
| ref / relation | EntityRefField | Entity reference picker |
| tags | TagsField | Tag 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
| Prop | Type | Description |
|---|---|---|
| schema | z.ZodObject | Zod schema to generate the form from |
| values | Record<string, unknown> | Controlled values (optional) |
| defaultValues | Record<string, unknown> | Initial values |
| onChange | (path, value) => void | Called on every field change |
| onSubmit | (values) => void | Called on form submission |
| columns | 1 | 2 | 3 | 4 | Number of form columns |
| groups | Group[] | Group fields into labeled sections |
| errors | Record<string, string> | External validation errors |
| disabled | boolean | Disable all fields |
| readOnly | boolean | Make all fields read-only |
| header | ReactNode | Content above the form |
| footer | ReactNode | Content 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.