Skip to main content
zod-to-form · MIT · Zod v4
zod-to-formzod-to-form

Schema in. Form out.
It's your code.

This is not a form library. It's a code generator that reads your Zod schemas and gives you production-ready React forms — with shadcn/ui, React Hook Form, and full TypeScript inference. Use the runtime renderer to iterate, then eject to generated code you fully own.

One walker. Three integration paths.

Walk your Zod schema once. Pick the path that fits: render at runtime, generate static code with the CLI, or let the Vite plugin compile on demand. Same config, same output — zero runtime dependency on @zod-to-form/* in the code you ship.

Config
z2f.config.ts
// z2f.config.ts
import { defineConfig } from '@zod-to-form/cli';

export default defineConfig({
components: {
SignupForm: {
ui: 'shadcn',
mode: 'submit',
},
},
});
vite.config.ts
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import z2fVite from '@zod-to-form/vite';

export default defineConfig({
plugins: [
// Presence of `generate` opts in to JSX scanning:
// the plugin finds <ZodForm schema={X}/> call sites
// and compiles them away at build time.
z2fVite({ generate: {} }),
react(),
],
});
Code
src/App.tsx (original)
// src/App.tsx
import { ZodForm } from '@zod-to-form/react';
import { signupSchema } from './schemas/signup';

export default function App() {
return (
<ZodForm
schema={signupSchema}
onSubmit={(data) => console.log(data)}
/>
);
}
Virtual module (compiled by plugin)
// Compiled on the fly by @zod-to-form/vite
// (virtual module — never hits disk, cached per schema)
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { signupSchema } from './schemas/signup.ts';

export default function Form(props) {
const form = useForm({
resolver: zodResolver(signupSchema),
});
return (
<form onSubmit={form.handleSubmit(props.onSubmit)}>
{/* <input>s generated from signupSchema … */}
</form>
);
}

Define your schema. Get a form.

No manual field wiring. Labels inferred, validation connected, types propagated to onSubmit.

SignupForm.tsx
import { z } from 'zod';
import { ZodForm } from '@zod-to-form/react';

const schema = z.object({
name: z.string().min(2),
email: z.string().email(),
role: z.enum(['admin', 'editor', 'viewer']),
});

export default function App() {
return (
<ZodForm
schema={schema}
onSubmit={(data) => console.log(data)}
/>
);
}

From prototype to production

Two paths, one config. Start fast, ship clean.

Rapid Builder

Runtime rendering

Drop in <ZodForm> and get a working form instantly. Perfect for admin panels, internal tools, and CRUD forms where speed matters.

  • Schema change → form updates on re-render
  • shadcn/ui preset — zero component config
  • Metadata via Zod v4 registry API
Production Team

CLI codegen

Generate static .tsx files you own. Review diffs, hand-edit, commit. The output has zero runtime dependency on zod-to-form.

  • Readable output — looks hand-written
  • --watch mode for development
  • CI-friendly — add to your build pipeline
Schema-First

Both paths, one config

Start with runtime for iteration. Eject to codegen when you need full control. Same z2f.config.ts drives both — component names and overrides carry over.

  • Codegen ↔ runtime parity
  • Custom field template via config
  • Tabs, accordions, steppers via component override

Faster than hand-wiring a form. By a lot.

We measure the full form lifecycle — mount + keystrokes + submit — against a hand-wired useForm + zodResolver baseline. The build-time walk eliminates per-mount optimizer cost, and native-rules mode bypasses Zod entirely for fields whose constraints can be expressed via minLength, pattern, min, max, and friends.

Form size (20 edits)Hand-wired + zodResolverz2f codegenSpeedupConfig
small (5 fields)502μs397μs1.26×codegen L1
medium (18 fields)1.53ms864μs1.77×codegen L2
large (50 fields)2.31ms1.68ms1.37×codegen L1

On heavy editing sessions (500 edits), the gap widens to 2.08× on medium forms and 2.00× on large forms — every keystroke in z2f costs ~100ns (a native-rule check) vs ~2.6μs (a full Zod parse) for the baseline. That's a 24× per-keystroke speedup on the hot path.

What sets zod-to-form apart

The Zod v4 form generation space has several players. None offer codegen, and none use the APIs Zod v4 designed for library authors.

Capabilityz2fAutoFormuniformsRJSF
Zod v4 substrate API
Build-time codegen
Zero-dependency eject
React Hook Form
shadcn/ui preset
Controlled component bridging
Field template customization
Discriminated unions
Typed recursive config

Built on the standards

No custom runtime. Just Zod, React Hook Form, and your component library.

Zod v4
React Hook Form
shadcn/ui
@hookform/resolvers
TypeScript
Radix UI

Stop wiring forms by hand

Define the schema. Get the form. Keep full control.