PettyUI/.firecrawl/pkgpulse-validation.md
Mats Bosson db906fd85a Fix linting config and package fields
- Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config)
  using direct eslint-plugin-solid + @typescript-eslint/parser approach
- Add @typescript-eslint/parser to root devDependencies
- Add main/module/types top-level fields to packages/core/package.json
- Add resolve.conditions to packages/core/vite.config.ts
- Create packages/core/tsconfig.test.json for test type-checking
- Remove empty paths:{} from packages/core/tsconfig.json
2026-03-29 02:35:57 +07:00

290 lines
9.2 KiB
Markdown

[Skip to main content](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026#main-content)
## [TL;DR](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#tldr)
**Zod v4 remains the default for TypeScript validation — but Valibot (8KB vs Zod's 60KB) and ArkType (fastest runtime parsing) are compelling for performance-critical use cases.** TypeBox generates JSON Schema natively, making it the best choice for OpenAPI/Swagger integration. For new projects: Zod v4. For edge/bundle-size-critical: Valibot. For OpenAPI: TypeBox. For maximum runtime speed: ArkType.
## [Key Takeaways](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#key-takeaways)
- **Zod v4**: 60KB, 10M+ downloads/week, best ecosystem (react-hook-form, trpc, drizzle)
- **Valibot**: 8KB, tree-shakable, modular API, ~10x smaller than Zod
- **ArkType**: Fastest parser (3-10x faster than Zod), TypeScript syntax strings
- **TypeBox**: JSON Schema native, `Static<typeof Schema>` TypeScript types
- **Performance**: ArkType > Valibot > TypeBox > Zod (but all are "fast enough" for most apps)
- **Ecosystem**: Zod integrates with everything; others are catching up
* * *
## [Downloads](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#downloads)
| Package | Weekly Downloads | Trend |
| --- | --- | --- |
| `zod` | ~10M | ↑ Growing |
| `@sinclair/typebox` | ~6M | ↑ Growing |
| `valibot` | ~1M | ↑ Fast growing |
| `arktype` | ~200K | ↑ Growing |
* * *
## [Performance Benchmarks](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#performance-benchmarks)
```
Schema: User object with 10 fields, nested address, array of tags
Parsing 100,000 objects:
ArkType: 45ms ← Fastest
Valibot: 120ms
TypeBox: 180ms
Zod v4: 280ms (v4 is 2x faster than v3's ~580ms)
Bundle size (minified + gzipped):
Valibot: 8KB ← Smallest
ArkType: 12KB
TypeBox: 60KB (includes JSON Schema types)
Zod v4: 60KB
Type inference speed (tsc, 50-field schema):
ArkType: ~200ms
Zod v4: ~450ms
Valibot: ~600ms
TypeBox: ~300ms
```
* * *
## [Zod v4: The Default](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#zod-v4-the-default)
```typescript
// Zod v4 — new features and performance improvements:
import { z } from 'zod';
// Basic schema (same as v3):
const UserSchema = z.object({
id: z.string().cuid2(),
email: z.string().email(),
name: z.string().min(2).max(100),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['user', 'admin', 'moderator']),
tags: z.array(z.string()).max(10),
address: z.object({
street: z.string(),
city: z.string(),
country: z.string().length(2), // ISO 2-letter
}).optional(),
metadata: z.record(z.string(), z.unknown()),
createdAt: z.coerce.date(), // Auto-coerce string → Date
});
type User = z.infer<typeof UserSchema>;
// Zod v4 new: z.file() for Blob/File
const UploadSchema = z.object({
file: z.instanceof(File)
.refine(f => f.size < 5_000_000, 'Max 5MB')
.refine(f => ['image/jpeg', 'image/png', 'image/webp'].includes(f.type), 'Must be JPEG/PNG/WebP'),
caption: z.string().max(500).optional(),
});
// Zod v4 new: z.pipe() for chained transforms
const ParsedDateSchema = z
.string()
.pipe(z.coerce.date()); // string → validated Date
// Zod v4 new: z.toJSONSchema()
const jsonSchema = z.toJSONSchema(UserSchema);
// Generates standard JSON Schema — useful for OpenAPI docs
// Error formatting (v4 — cleaner):
const result = UserSchema.safeParse({ email: 'bad' });
if (!result.success) {
const errors = result.error.flatten();
// { fieldErrors: { email: ['Invalid email'] }, formErrors: [] }
}
```
* * *
## [Valibot: Bundle-Size Champion](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#valibot-bundle-size-champion)
```typescript
// Valibot — modular, tree-shakable:
import {
object, string, number, array, optional, enum_,
email, minLength, maxLength, integer, minValue, maxValue,
parse, safeParse, flatten,
type InferInput, type InferOutput,
} from 'valibot';
// Only imports what you use — tree-shaking reduces bundle to ~2-5KB for simple schemas
const UserSchema = object({
id: string([minLength(1)]),
email: string([email()]),
name: string([minLength(2), maxLength(100)]),
age: optional(number([integer(), minValue(0), maxValue(150)])),
role: enum_(['user', 'admin', 'moderator']),
tags: array(string(), [maxLength(10)]),
});
type User = InferInput<typeof UserSchema>;
// Parse (throws on error):
const user = parse(UserSchema, rawData);
// Safe parse (returns result/error):
const result = safeParse(UserSchema, rawData);
if (result.success) {
console.log(result.output);
} else {
const errors = flatten(result.issues);
// { nested: { email: ['Invalid email'] } }
}
```
```typescript
// Valibot with React Hook Form:
import { valibotResolver } from '@hookform/resolvers/valibot';
import { useForm } from 'react-hook-form';
function SignupForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: valibotResolver(UserSchema),
});
return (
<form onSubmit={handleSubmit(console.log)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
</form>
);
}
```
* * *
## [ArkType: Fastest Runtime + TypeScript Syntax](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#arktype-fastest-runtime--typescript-syntax)
```typescript
// ArkType — TypeScript-syntax strings for schemas:
import { type } from 'arktype';
// Syntax feels like writing TypeScript:
const User = type({
id: 'string',
email: 'string.email',
name: '2 <= string <= 100', // min/max length shorthand!
age: 'number.integer | undefined',
role: '"user" | "admin" | "moderator"',
tags: 'string[] <= 10', // array with max length
createdAt: 'Date',
});
type User = typeof User.infer;
// Parse:
const result = User(rawData);
// ArkType returns morph (with parse) or error:
if (result instanceof type.errors) {
console.log(result.summary); // Human-readable error
} else {
// result is User
}
// ArkType advanced: morphs (transform)
const ParsedDate = type('string').pipe(s => new Date(s), 'Date');
// Recursive types (Zod struggles here):
const TreeNode = type({
value: 'number',
children: 'TreeNode[]', // Self-referencing!
}).describe('TreeNode'); // Named for error messages
```
* * *
## [TypeBox: JSON Schema Native](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#typebox-json-schema-native)
```typescript
// TypeBox — generates JSON Schema, used in Fastify/Hono:
import { Type, Static } from '@sinclair/typebox';
import { Value } from '@sinclair/typebox/value';
// TypeBox schema IS JSON Schema:
const UserSchema = Type.Object({
id: Type.String({ format: 'uuid' }),
email: Type.String({ format: 'email' }),
name: Type.String({ minLength: 2, maxLength: 100 }),
age: Type.Optional(Type.Integer({ minimum: 0, maximum: 150 })),
role: Type.Union([\
Type.Literal('user'),\
Type.Literal('admin'),\
Type.Literal('moderator'),\
]),
tags: Type.Array(Type.String(), { maxItems: 10 }),
});
// TypeScript type from schema:
type User = Static<typeof UserSchema>;
// Validate:
const result = Value.Check(UserSchema, rawData);
if (!result) {
const errors = [...Value.Errors(UserSchema, rawData)];
// [{ path: '/email', message: 'Expected string' }]
}
// TypeBox + Hono (validated routes with OpenAPI):
import { Hono } from 'hono';
import { describeRoute } from 'hono-openapi';
const app = new Hono();
app.post('/users',
describeRoute({
requestBody: { content: { 'application/json': { schema: UserSchema } } },
responses: { 201: { description: 'Created' } },
}),
async (c) => { /* handler */ }
);
// Export OpenAPI spec:
// app.doc('/openapi.json', { openapi: '3.0.0', info: { title: 'API', version: '1' } })
```
* * *
## [Decision Guide](https://www.pkgpulse.com/blog/zod-v4-vs-arktype-vs-typebox-vs-valibot-schema-2026\#decision-guide)
```
Use Zod v4 if:
→ Default choice — best ecosystem (react-hook-form, trpc, drizzle, next-safe-action)
→ Team already knows Zod v3 (v4 is mostly backwards compatible)
→ Need broad library compatibility
→ Bundle size is not a constraint
Use Valibot if:
→ Edge runtime / bundle size critical (<5KB budget)
→ Want tree-shakable, pay-only-for-what-you-use
→ Cloudflare Workers or similar constrained environments
Use ArkType if:
→ Parsing millions of objects (backend hot path)
→ Love TypeScript-native syntax strings
→ Need recursive types easily
→ Fastest possible validation
Use TypeBox if:
→ Building OpenAPI/Swagger documentation
→ Using Fastify (TypeBox is Fastify's native schema)
→ Need JSON Schema output for other tools
→ API validation that also generates docs
```
_Compare Zod, Valibot, ArkType, and TypeBox on [PkgPulse](https://pkgpulse.com/compare/zod-vs-valibot)._
## Comments
### The 2026 JavaScript Stack Cheatsheet
One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.
Get the Free Cheatsheet