Skip to content

perf(validation): migrate zod → valibot (−17.9 kB gz off public form)#91

Merged
Vijayabaskar56 merged 5 commits into
mainfrom
perf/zod-to-valibot-migration
May 28, 2026
Merged

perf(validation): migrate zod → valibot (−17.9 kB gz off public form)#91
Vijayabaskar56 merged 5 commits into
mainfrom
perf/zod-to-valibot-migration

Conversation

@Vijayabaskar56
Copy link
Copy Markdown
Member

What

Migrates validation from zod → valibot across the entire app. zod was the single largest dependency in the public-form client bundle, and valibot's modular/tree-shakeable Standard Schema plugs directly into TanStack Router validateSearch, Start createServerFn().inputValidator, TanStack DB collection schemas, and TanStack Form — no adapter needed.

Result — public-form index chunk

Baseline (zod) After (valibot) Δ
gzip 280.87 kB 262.99 kB −17.88 kB (−6.4%)
raw 1,106.79 kB 1,041.03 kB −65.76 kB
zod in index 23.59 kB gz 0 eliminated
valibot in index 0 3.02 kB gz +3.02

zod is fully removed from the public-form critical path and from our direct dependencies (it remains only as a transitive dep of the AI SDK packages, in the lazy editor chunk).

Scope

  • Client routes ($shortId, $slug, f/$formId, login, login/email, form-builder edit): zodValidator → valibot Standard Schema in validateSearch. Dropped @tanstack/zod-adapter.
  • Shared helpers (src/lib/valibot-search.ts): verified zod-parity helpers for coerce/.default/.catch.
  • Local draft collection (collections/local/form.ts): TanStack DB schema.
  • All server-fn inputValidators + dynamic preview-form schema.
  • AI schemas (ops-schema.ts, form-generate.ts): valibot, wrapped with valibotSchema() from @ai-sdk/valibot@2.0.28 (ai-v6 line; deps the @ai-sdk/provider-utils@4.0.27 we already use) so the AI SDK gets JSON Schema via @valibot/to-json-schema. Raw valibot can't be passed to the AI SDK (its ~standard lacks jsonSchema); valibotSchema() is required — conversion verified at runtime.

Verification

  • pnpm exec tsc --noEmit: 0 errors
  • pnpm exec vitest run --no-file-parallelism: 422/422 pass (also green in pre-push)
  • oxlint / oxfmt / knip: clean

Deps

  • Added: valibot@1.4.0, @ai-sdk/valibot@2.0.28, @valibot/to-json-schema@1.7.0
  • Removed: zod, @tanstack/zod-adapter

First step of the zod->valibot migration. Converts the public-form-reachable
validation (shortIdSchema, public-form-view inputValidators, $shortId
validateSearch) to valibot's Standard Schema — plugs into TanStack Router
validateSearch and Start inputValidator with no adapter (drops zod-adapter here).

POC measurement (public-form index chunk): valibot adds 1.3 kB gz; zod's
23.6 kB stays until all client-reachable z.* call-sites are migrated. Net win
(~16-20 kB gz off the index) realizes only when zod is fully evicted.
Converts all 5 zodValidator routes ($slug, f/$formId, login, login/email,
form-builder edit) + refactors $shortId to a shared helper. Adds
src/lib/valibot-search.ts with verified zod-parity helpers for coerce/default/
catch. valibot Standard Schema works directly in validateSearch, so
@tanstack/zod-adapter is now fully unused and removed.
FormSchema (localStorage draft collection) -> valibot Standard Schema, which
TanStack DB accepts directly. Maps z.custom/.nullable().optional()/.default/
.transform faithfully (v.custom/v.nullish/v.optional w/ default/v.pipe+transform).
…to valibot

Converts all server-fn inputValidators (forms, analytics, submissions,
custom-domains, workspaces, form-versions, notifications, favorites, billing,
uploads, public-*, visit-end beacon) and the dynamic preview-form schema to
valibot Standard Schema (works directly in createServerFn().inputValidator and
TanStack Form validators).

AI-SDK-bound schemas (ops-schema.ts, form-generate.ts) deliberately stay on zod:
AI SDK v6 builds JSON Schema via schema['~standard'].jsonSchema, which valibot
1.4.0 does not expose, so streamObject/tool/useObject would throw at runtime.
The only stable valibot adapter (@ai-sdk/valibot) is 3.0.0-canary.
…remove zod

ops-schema.ts + form-generate.ts now use valibot; schemas passed to AI SDK
(streamObject/tool/useObject) are wrapped with valibotSchema() from
@ai-sdk/valibot@2.0.28 (ai-v6 line, deps the provider-utils@4.0.27 we already
have), which supplies the JSON Schema the SDK sends to the model via
@valibot/to-json-schema. Verified the conversion produces valid JSON Schema for
formGenSchema/freeThemeSchema/themeTokensSchema at runtime.

zod is fully removed from source and from our direct deps (remains only as a
transitive dep of the AI SDK packages). Completes the zod->valibot migration.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
reform Ready Ready Preview, Comment May 27, 2026 5:47pm

Request Review

@Vijayabaskar56 Vijayabaskar56 merged commit a1854d2 into main May 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant