Core Concepts
Schema
How Zod schemas drive form field generation
Form Wire introspects your Zod schema to generate field metadata automatically. You don't define fields manually — the schema is the source of truth.
Supported Field Types
| Zod Type | Form Field Kind | UI Control |
|---|---|---|
z.string() | string | <input type="text"> |
z.email() | string | <input type="email"> |
z.coerce.number() | number | <input type="number"> |
z.coerce.date() | date | <input type="date"> |
z.boolean() | boolean | <input type="checkbox"> |
z.literal(true) | boolean | <input type="checkbox"> (must be checked) |
z.enum([...]) | select | <select> |
z.literal("value") | select | <select> (single option) |
z.file() | file | <input type="file"> |
z.string().array() | stringArray | Repeated text inputs |
z.object({...}).array() | objectArray | Fieldset groups |
z.object({...}) | Nested group | <fieldset> |
String Subtypes
Form Wire detects common string formats and maps them automatically:
z.email() // → type="email"
z.coerce.number() // → type="number"
z.coerce.date() // → type="date"
z.string() // → type="text"Force a textarea using the component option in field config:
createFormWire(schema, {
fields: {
bio: { label: "Bio", component: "textarea" },
},
});Optional vs Required
Zod determines whether a field is required:
z.string() // required (must be non-empty)
z.string().optional() // not required
z.string().nullable() // not required, parses as null when emptySelect Fields
Any union of string literals generates a <select>:
z.enum(["pending", "active", "paused"])
// → <select> with three optionsEnum with Labels
Option labels come from the field config options or are generated from the enum values:
createFormWire(schema, {
fields: {
status: {
label: "Status",
placeholder: "Choose a status",
},
},
});Schema Metadata
Use withMeta() to attach label and description directly to schema nodes:
import { withMeta } from "@form-wire/core";
const schema = z.object({
name: withMeta(z.string().min(1), {
label: "Full Name",
description: "Your legal name",
placeholder: "Ada Lovelace",
}),
});Metadata from withMeta is merged with field config from createFormWire — config takes precedence.