.agents/skills/zod-to-form/SKILL.md
Guide for configuring and using @zod-to-form to generate React Hook Form components from Zod schemas. Use when (1) setting up z2f config (defineConfig, field mappings, hidden fields, per-schema overrides), (2) generating forms with z2f CLI, (3) creating custom field adapters (TypeSelector, CardinalitySelector) that bridge controlled/uncontrolled components, (4) troubleshooting generated form output (missing fields, wrong components, unwanted fields appearing), or (5) understanding the z2f architecture (core walker, CLI codegen, runtime renderer).
npx skillsauth add pradeepmouli/zod-to-form zod-to-formInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Generate type-safe React Hook Form components from Zod schemas at build time.
@zod-to-form/core Schema walker & FormField[] IR (zero deps, zod peer)
@zod-to-form/react Runtime <ZodForm> renderer (peer deps only)
@zod-to-form/cli Build-time codegen CLI -> .tsx files
The CLI reads Zod schemas, walks them with the core walker to produce FormField[], then renders each field using configurable templates into standalone React components.
# Initialize config
npx z2f init
# Generate forms
npx z2f generate --schema ./src/generated/zod-schemas.ts --out ./src/components/forms/generated
z2f.config.ts)Use defineConfig from @zod-to-form/core. See references/config-reference.md for full API.
import { defineConfig } from '@zod-to-form/core';
export default defineConfig({
components: '@/components/zod-form-components',
formPrimitives: { field: 'Field', label: 'FieldLabel', control: 'FieldControl' },
defaults: { mode: 'auto-save', ui: 'shadcn', overwrite: true, serverAction: false },
include: ['DataSchema', 'UserSchema'],
exclude: [],
fieldTypes: { /* custom component registry */ },
fields: { /* global field mappings */ },
schemas: { /* per-schema overrides */ }
});
'name', '$type''typeCall.type', 'output.card''attributes[].name', 'enumValues[].display''attributes[].typeCall.type'Always use [] bracket notation for arrays in config, never ${index}.
hidden: true completely excludes a field from generated output (no JSX, no register(), no import):
fields: {
$type: { hidden: true }, // type discriminator
'attributes[].$type': { hidden: true }, // nested discriminator
'attributes[].typeCall.arguments': { hidden: true }, // internal array
annotations: { hidden: true }, // custom-rendered section
conditions: { hidden: true }, // custom-rendered section
}
Override global mappings for specific schemas. Use order for field sequence within array items:
schemas: {
DataSchema: {
fields: {
'attributes[].name': { fieldType: 'Input', order: 1 },
'attributes[].typeCall.type': { fieldType: 'TypeSelector', order: 2 },
'attributes[].card': { fieldType: 'CardinalitySelector', order: 3 },
'attributes[].override': { hidden: true }
}
}
}
Per-schema overrides take precedence over global fields.
By default, generated forms use register() (uncontrolled). For components that need controlled behavior (Select, Combobox, custom widgets), mark them controlled: true in fieldTypes:
fieldTypes: {
Input: { component: 'Input' }, // uncontrolled (register)
TypeSelector: { component: 'TypeSelector', controlled: true }, // controlled (Controller)
CardinalitySelector: { component: 'CardinalitySelector', controlled: true },
}
The codegen automatically generates a <Controller> pattern for controlled components — no forwardRef or manual useController adapter needed.
Remap RHF field props (value, onChange, onBlur) to component-specific prop names:
fieldTypes: {
MyCombobox: {
component: 'MyCombobox',
controlled: true,
propMap: { onSelect: 'field.onChange', selectedValue: 'field.value' }
}
}
propMap can also be set per-field in the fields or schemas config.
z2f init auto-detects controlled components by:
Forms support both controlled (values prop) and uncontrolled (defaultValues prop) modes:
<MyForm
values={storeData} // controlled from store
onValueChange={(data) => update(data)} // auto-save callback
/>
In auto-save mode, a watch() subscription fires onValueChange on every field change.
| Symptom | Fix |
|---------|-----|
| Internal fields showing | Add hidden: true with bracket notation path |
| Wrong component rendered | Check bracket notation: items[].field not items.0.field |
| Custom component not receiving value | Mark controlled: true in fieldTypes |
| Empty nested block in output | All children hidden but parent not removed — upgrade z2f |
| Per-schema override ignored | Check exact path match; per-schema takes precedence |
z2f init # Create z2f.config.ts
z2f generate # Generate forms from config
z2f generate --watch # Watch mode
z2f generate --dry-run # Preview without writing
tools
Use when working with zod-to-form (core, react, cli, codegen, vite).
tools
Vite plugin for zod-to-form — transforms ?z2f imports into generated form components and optionally replaces <ZodForm> JSX call sites with generated components at build time Use when: You want `import SignupForm from './signup.schema?z2f'` to Just Work in a.... Also: vite, vite-plugin, zod, zod-v4, codegen, forms, form-generation, schema-driven, react-hook-form, build-plugin, jsx-transform.
development
Runtime <ZodForm> renderer for Zod v4 schemas Use when: You need form rendering in storybook, playgrounds, or low-traffic admin UIs —.... Also: zod, zod-v4, react, forms, form-generation, react-hook-form, schema-driven, dynamic-forms, form-renderer, hookform-resolver, zod-form-renderer.
development
Schema walker and processor registry for Zod v4 form generation Use when: You want per-field validation instead of whole-form validation. Also: zod, zod-v4, forms, form-generation, schema, schema-walker, processor-registry, react-hook-form, schema-driven, form-schema, zod-registry.