← Default view
DOCDS-VSL-2607
REV1.1 — SPEC v1.1.0 SOCKET
ISSUED13 JUL 2026
CLASSSurvey · 33 sources
SUPERSEDESALL PRE-STANDARD-SCHEMA REVISIONS

Schema
Validators Interchangeable validation components for React forms.
Socket-compatible with StandardSchemaV1. Three parts characterised;
two legacy parts retained for repair only.

A socketed DIP chip seated on a circuit board
FIG. 1  The part is socketed, not soldered. Standard Schema v1 made the validator a field-replaceable unit.
▲ Selection guide — read this first

Fit Zod 4 unless you have a reason not to. It is the only part whose ecosystem — drizzle-zod, tRPC, the Next.js docs, the AI SDKs — covers the whole shared-schema path from DB row to form field [17] [19].

Fit Valibot when the schema is client-bundled and the kilobytes are load-bearing — 1.37 kB vs 17.7 kB for a login form [8]. Fit ArkType when you validate at volume or want TS-syntax schemas — ~4× Zod 4 throughput [20].

The 2026 twist: because Standard Schema v1 is near-universal, picking wrong is cheap at the form-library boundary and expensive at the shared-schema boundary. The form library no longer cares which part you seat. Your ORM, your RPC layer and your OpenAPI generator still do.

01

Characterised parts

July 2026 · current majors
Two loose DIP integrated circuits

Three parts. One footprint. Same pinout since Jan 2025.

FIG. 1a — Before the spec, each of these needed its own adapter at every board it touched. @hookform/resolvers still carries 20 of them [14], for the boards that predate the socket.
ZOD
4.4.3 · 43.3K★
ZOD-4.4.3 ~224M / wk [24]
◆ Recommended for new designs
17.7 kB gz
login-form schema [8]

The ecosystem part. Out-downloads React itself (~224M vs ~153M weekly) [24]. The v4 rewrite closed both historical objections: 6.5–14.7× runtime and 10× less tsc thrash on .extend() chains [4].

Throughput~2M ops/s [20]
Locales40+ built in [5]
Tree-shakeonly via zod/mini [7]
SocketStandardSchemaV1 (co-author)
VALIBOT
1.4.2 · 8.8K★
VLB-1.4.2 ~14.3M / wk [25]
◆ Low-power / bundle-critical
1.37 kB gz
same schema, 13× smaller [8]

Tree-shakeable by construction — one exported function per action, so the client only pays for what it references [8]. The input/output split is impossible to get wrong. Its own docs rate its speed "midfield."

Throughput~4M ops/s [20]
Locales@valibot/i18n [10]
Tree-shake✓ by design
SocketStandardSchemaV1 (co-author)
ARKTYPE
2.2.3 · 7.8K★
ARK-2.2.3 ~1.2M / wk [26]
◆ High-throughput / TS-native
~8M ops/s
5-field object, ~4× Zod 4 [20]

Schemas are TypeScript syntax in a string; inference is 1:1 [23]. The price is on the bench, not the wire: a type-level parser plus a JIT runtime that ships whole (~20 kB) regardless of how little you use [20].

Bundle~20 kB, monolithic [20]
Localesnone [13]
tsc costhighest of the three
SocketStandardSchemaV1 (co-author)
02

Rated characteristics

every figure traceable to its citation
Parameter Sym. Zod 4 ZOD-4.4.3 Valibot 1 VLB-1.4.2 ArkType 2 ARK-2.2.3 Conditions / note
Bundle, gzip Bgz 17.7 kB [8]
Zod Mini: 6.88 kB
1.37 kB [8]
~20 kB [20]
parser + JIT ship regardless
Login-form schema, esbuild. Yup ~12 kB; Joi ~145 kB.
Tree-shakeable ηts classic method chains; via zod/mini [7] ✓ by design [8] ✗ monolithic runtime Valibot exports one function per action; nothing you don't import is emitted.
Throughput fval ~2M ops/s [20]
6.5–14.7× vs Zod 3 [4]
~4M ops/s [20]
~8M ops/s [20]
5-field object. Zod 3 sat at ~300K. On a form, none of this is your bottleneck.
TS inference τinf Excellent — z.input / z.output split [6] Excellent — InferInput / InferOutput [9] 1:1 with TS syntax [23] Joi cannot infer values from a schema at all [14].
tsc / editor cost Ttsc 4000 ms → 400 ms vs v3 on .extend() chains [4]
Low — plain generics
⚠ Highest — type-level parser; big schemas need .scope() [20]
This is the cost you pay every keystroke, not every request.
Error customisation Ecfg Unified error param; schema → per-parse → z.config() → locale [5] setSpecificMessage / setSchemaMessage / setGlobalMessage [10] arktype/config global, .configure() per type — shallow only [13] Yup/Joi: per-rule message strings.
i18n locales Nloc 40+ in zod/locales, z.config(de()) [5] @valibot/i18n, modular per-language/per-action [10] ✗ no locales package
docs: "still a topic we're working on" [13]
Load-bearing: the socket cannot translate for you. See §03.
Form coercion Cstr z.coerce.*, z.stringbool(), z.preprocess() [6] v.pipe(v.string(), v.toNumber()) [29] type("string.numeric.parse"), filter / narrow [12] Yup coerces implicitly — lossy.
JSON Schema out Jout z.toJSONSchema() [4] @valibot/to-json-schema ✓ bidirectional [11] Spec v1.1 standardises this pin; Joi 18 already ships it [22].
03

Socket specification

StandardSchemaV1 · ~60 lines of TypeScript
A row of empty DIP sockets in ascending pin counts, laid on graph paper
FIG. 2  The socket, empty. Zod, Valibot and ArkType maintainers co-authored it — then each built a part that drops into it.

Standard Schema v1.1.0

⭐ 3.6k · ~69M/wk · Dec 2025 [3] · no adapter, no peer dep, no shim

A schema exposes a ~standard property. A consumer calls schema["~standard"].validate(input) and gets back { value } or { issues } [16]. That is the whole interface.

1
~standard.validate(input)

Sync or async. Returns { value } on success, { issues } on failure. This one pin is why any form library can accept any of the five parts.

2
Issue.message  Issue.path

The entire error surface.

N/C
Issue.code  Issue.params

Not connected — and this is the consequence nobody talks about. The spec's issue type is { message: string; path?: … }: no error code, no params [2]. A Standard-Schema-consuming form library therefore cannot re-word or translate your errors. i18n must happen inside the part. That is what makes ArkType's missing locales package a real cost, not a footnote.

3
~standard.jsonSchema.input() / .output()

Added in spec v1.1 (Dec 2025) [3]. Joi 18 already implements it [22]. This is the pin that will eventually drive OpenAPI, LLM tool-calling and form generation from one schema, without vendor-specific toJSONSchema calls.

04

Compatible host boards

what accepts the socket, what is soldered to Zod

React Hook Form ⭐ 44.8k

Resolver socket

@hookform/resolvers 5.4.0 ⭐ 2.3k ships 20 library-specific resolvers plus a generic standardSchemaResolver [14] [32]. ⚠ ArkType's dedicated resolver is firstError-only; the Standard Schema one supports firstError | all.

TanStack Form ⭐ 6.6k

Native socket

Pass the schema straight into validators.onChange. Zod, Valibot, ArkType, Effect/Schema — all adapter-free [15].

Conform ⭐ 2.6k

FormData socket

parseWithZod (and the Valibot equivalent) runs the same schema in onValidate on the client and inside the Server Action [18] [30].

next-safe-action ⭐ 3k

Native socket

Standard Schema at the protocol level — .inputSchema() takes any compliant schema. You can mix Zod and Valibot across actions in one project [16].

Next.js docs

Soldered to Zod

Still hard-codes Zod (safeParse + flatten().fieldErrors + useActionState) — and the 16.2 sample still uses the removed v3 invalid_type_error option [17].

tRPC 11 · Hono · oRPC · Drizzle

Mixed

Standard Schema or per-library packages. ArkType ships drizzle-arktype and @hono/arktype-validator [11]. Coverage outside Zod is real but thinner — see the field report below.

◆ Field report — the boundary that actually bites

A practitioner migrated two forms to Valibot: ~18 kB gzip and ~140 ms LCP saved. Then stopped — because drizzle-valibot lagged drizzle-zod on a nullable JSONB column, and the schema was shared with the DB [19].

At the form-library boundary the choice is free. At the ORM / RPC / codegen boundary it is not. Standard Schema makes the form library indifferent; it does nothing to make drizzle-zod, zod-openapi or an AI SDK's tool schemas indifferent.

05

Application note — strings in, typed data out

where most 2026 form bugs live

Every <input> yields a string. Every part therefore has an input type ≠ output type problem. The three handle it in three different ways, and one of them has a trap in the obvious move.

Circuit A — one schema, two runtimesrecommended
// schema.ts — no 'use server', no 'use client'
export const signup = z.object({
  email: z.email(),
  age:   z.coerce.number().min(18),
});

// action.ts
'use server';
export async function signupAction(_prev, fd) {
  const r = signup.safeParse(Object.fromEntries(fd));
  if (!r.success)
    return { errors: z.flattenError(r.error).fieldErrors };
  // r.data is typed z.output<typeof signup>
}
z.flattenError() / z.treeifyError() replace v3's .flatten() / .format(); flattenError yields { formErrors, fieldErrors } — exactly the shape useActionState hands back to the client [33]. ⚠ The whole schema module lands in the client bundle: keep DB imports out of it.
Circuit B — the three-generic formRHF
const schema = z.object({ age: z.coerce.number().min(18) });

// RHF holds z.input, hands you z.output in onSubmit
useForm<
  z.input<typeof schema>,
  unknown,
  z.output<typeof schema>
>({
  resolver: standardSchemaResolver(schema),
});
Resolvers v5 infers both sides from the schema, so the explicit generics are only needed when you wrap useForm yourself [32] [6]. For checkboxes and env-ish strings, z.stringbool() beats a hand-rolled preprocessor [6].
Zod 4
z.string().transform(Number).pipe(z.number())
⚠ Trap. z.coerce.number() is the obvious move and the wrong one for forms: its input type is unknown [6], which erases the compile-time contract with your form state. Prefer the transform+pipe above, or z.preprocess.
Valibot 1
v.pipe(v.string(), v.toNumber(), v.minValue(18))
Explicit by construction: InferInput = string, InferOutput = number [9] [29]. Verbose — and impossible to get wrong.
ArkType 2
type("string.numeric.parse")
Morphs, with a filter / narrow split — validate before vs after the morph [12]. A filter rejects a 10 MB string before you parse it; a narrow checks the resulting number. The cleanest model of the three.
Absolute maximum ratings — exceeding these will produce bugs

TanStack Form drops transformed values

Its validators do not hand back transformed output [15]. The schema validates; you re-parse in onSubmit if you want the coerced value. Budget for that.

Conform inverts the contract

parseWithZod introspects the schema and injects coercion + empty-string stripping, so a plain z.number() works against raw FormData [18]. The only one that makes FormData first-class.

ArkType's RHF resolver is firstError-only

Use the generic standardSchemaResolver instead if you need all-error mode [14].

i18n cannot live downstream of the socket

The Standard Schema Issue carries only message + path [2]. A form library below the spec has nothing left to translate with. Do i18n inside the part, whichever part you seat.

06

Legacy inventory

Yup 1.7.1 · Joi 18.2.3
Two old integrated circuits in a small cardboard parts tray
Not recommended
for new designs
FIG. 3  Old stock, still in the tray. Now socket-compatible — which is a migration lifeline, not a reason to order more.

Both now speak the socket. Neither is a starting choice.

Yup 1.7.1 [27] ⭐ 23.7k and Joi 18.2.3 [28] ⭐ 21.2k both implement Standard Schema [21] [22]. That means an existing Yup or Joi codebase can adopt TanStack Form or next-safe-action without rewriting a single schema. Keep them if you have them; don't start with them.

YUP-1.7.1  ~10.1M/wk
  • ~12 kB, not tree-shakeable
  • Runtime ≈ Zod 3 tier
  • Coerces implicitly — lossy
  • Inference: ✓
JOI-18.2.3  ~17.8M/wk
  • ~145 kB, Node-oriented
  • Ships the spec's JSON Schema converter [22]
  • Cannot infer values from a schema [14]
  • Server-only in practice

Joi's missing inference breaks the "typed data out" half of the contract outright — the part fits the socket, but the board downstream gets any.

07

Ordering information

pick the part that survives contact with your server stack
1
New app · shared client + server schemas
Order ZOD-4.4.3. The v4 rewrite closed both historical objections (6.5–14.7× runtime, 10× less tsc thrash on .extend() chains, half the bundle) [4], it has 40+ locales built in [5], and everything downstream of your form — ORM, RPC, OpenAPI, LLM tools — assumes it.
2
Bundle-critical · client-only schemas
edge, mobile web, embeddable widget
Order VLB-1.4.2. 1.37 kB vs 17.7 kB is not a rounding error [8], and it pairs cleanly with Conform and TanStack Form. Reach for zod/mini [7] instead only if you refuse a second library — its DX is worse than Valibot's for the same win.
3
High-throughput validation · TS-native schema syntax
Order ARK-2.2.3. ~4× Zod 4 [20]. Budget for compile-time cost on large schemas and for rolling your own i18n [13].
Yup / Joi
Don't start here. Standard Schema support is a migration lifeline — it lets an existing codebase adopt a modern form library without rewriting schemas [21] [22]. It is not a reason to pick them.
§
All configurations
Do i18n inside the schema library. The Standard Schema Issue carries only message + path [2] — a form library downstream of the spec has nothing left to translate with. Or split deliberately (Valibot client / Zod server) and accept translating at the boundary.
08

Related datasheets

Building forms in React in 2026 — series index
09

Source register

33 citations · every figure above traces to one
DS-VSL-2607 · Rev 1.1 · Issued 13 Jul 2026 Figures 1–3: Wikimedia Commons Full canonical write-up → Atlas