Website & Live Playground: https://vld.oxog.dev
- Release-Gated Speed: 11x+ faster runtime throughput and 4.7x+ less memory consumption compared to Zod.
- Zero Dependencies: Pure TypeScript/JavaScript with zero third-party runtime bloat.
- Drop-in Zod Compatibility: Swap imports directly or use subpaths (
@oxog/vld/v4,@oxog/vld/mini,@oxog/vld/v4/core,@oxog/vld/v4/locales). - Full Static Inference: Automatic type extraction using
v.infer<typeof schema>. - Tree-Shakeable Mini API: Build hyper-optimized bundles with
@oxog/vld/mini. - Built-in i18n: Out-of-the-box error localization for 27+ languages with lazy-loading support (
@oxog/vld/locales/lazy). - Result Pattern & Codecs: Functional error handling (
tryCatch,match,Ok,Err) and bidirectional data transformations. - 100% Test Coverage: Verified across 2500+ tests and drop-in TypeScript application suites.
npm install @oxog/vld
# or
yarn add @oxog/vld
# or
pnpm add @oxog/vld
# or
bun add @oxog/vldimport { v } from '@oxog/vld';
// Define schema with chainable validations
const userSchema = v.object({
name: v.string().min(2).max(100),
email: v.string().email(),
age: v.number().int().positive().optional(),
role: v.enum('admin', 'user', 'guest').default('user'),
tags: v.array(v.string()).min(1),
});
// Infer TypeScript type
type User = v.infer<typeof userSchema>;
// Safe parsing (no throwing)
const result = userSchema.safeParse({
name: 'John Doe',
email: 'john@example.com',
age: 28,
tags: ['developer', 'typescript'],
});
if (result.success) {
console.log('Valid data:', result.data); // Typed as User
} else {
console.error('Validation issues:', result.error.issues);
}v.string() // String validation
v.number() // Number validation
v.int() // Integer validation
v.int32() // 32-bit integer validation
v.boolean() // Boolean validation
v.bigint() // BigInt validation
v.date() // Date validation
v.symbol() // Symbol validation
v.uint8array() // Uint8Array validation
v.literal('active') // Literal value
v.enum('admin', 'user', 'guest') // Enum values
v.any() // Any type
v.unknown() // Unknown type
v.null() // Null
v.undefined() // Undefined
v.nullish() // Null or undefined
v.void() // Void
v.never() // Neverv.string()
.min(3)
.max(100)
.email()
.url()
.uuid()
.regex(/^[a-z0-9]+$/)
.startsWith('https://')
.endsWith('.json')
.trim()
.toLowerCase();
// Top-level format helpers:
v.email()
v.uuid()
v.creditCard() // Luhn checksum validated
v.jwt()
v.cuid()
v.cuid2()
v.nanoid()
v.ulid()
v.ipv4()
v.ipv6()
v.iso.date()
v.iso.dateTime()v.number()
.min(0)
.max(100)
.int()
.positive()
.negative()
.nonnegative()
.multipleOf(5)
.finite()
.safe();// Objects
const profileSchema = v.object({
username: v.string().min(3),
age: v.number().optional(),
});
// Object transformations
profileSchema.partial(); // All fields optional
profileSchema.strict(); // Reject unknown fields
profileSchema.passthrough(); // Keep unknown fields
profileSchema.pick('username');
profileSchema.omit('age');
profileSchema.extend({ bio: v.string() });
// Arrays & Collections
v.array(v.string()).min(1).max(10);
v.tuple(v.string(), v.number());
v.record(v.string(), v.number());
v.set(v.string());
v.map(v.string(), v.number());// Union
v.union(v.string(), v.number());
// Discriminated Union
const eventSchema = v.discriminatedUnion('type',
v.object({ type: v.literal('click'), x: v.number(), y: v.number() }),
v.object({ type: v.literal('scroll'), offset: v.number() })
);
// Intersections & XOR
v.intersection(schemaA, schemaB);
v.xor(schemaA, schemaB);
// Recursive / Lazy Schemas
const treeSchema: ReturnType<typeof v.lazy> = v.lazy(() =>
v.object({
id: v.string(),
children: v.array(treeSchema).optional(),
})
);v.coerce.string().parse(123); // "123"
v.coerce.number().parse("42"); // 42
v.coerce.boolean().parse("true"); // true
v.coerce.bigint().parse("1000"); // 1000n
v.coerce.date().parse("2026-08-17"); // Date objectconst customSchema = v.string()
.transform(val => val.trim())
.refine(val => val.length >= 3, 'Must be at least 3 characters')
.default('default_value')
.catch('fallback_on_error');
// SuperRefine for multi-field cross validation
const passwordSchema = v.object({
password: v.string().min(8),
confirm: v.string(),
}).superRefine((data, ctx) => {
if (data.password !== data.confirm) {
ctx.addIssue({
code: 'custom',
path: ['confirm'],
message: 'Passwords do not match',
});
}
});For bundle-constrained applications, @oxog/vld/mini provides pure standalone functions with zero extra overhead:
import { string, number, object, optional, array } from '@oxog/vld/mini';
const userSchema = object({
name: string().min(2),
age: optional(number().positive()),
roles: array(string()),
});VLD provides drop-in subpaths that mirror Zod export structures and error shapes:
// Replace Zod imports seamlessly
import { z } from '@oxog/vld';
import * as core from '@oxog/vld/v4/core';
import * as mini from '@oxog/vld/v4-mini';
import * as locales from '@oxog/vld/v4/locales';
import { v, deepPartial, input, output } from '@oxog/vld';- Structured error issues with
expected,received,minimum,maximum, andpath. - Compatibility tested against latest stable Zod releases.
import { v, VldError, treeifyError, prettifyError, flattenError } from '@oxog/vld';
const result = userSchema.safeParse(invalidData);
if (!result.success) {
const error = result.error as VldError;
// Flattened field errors for forms
const { fieldErrors, formErrors } = flattenError(error);
// Human-readable CLI / console output
const pretty = prettifyError(error);
// Nested tree structure for UI inspection
const tree = treeifyError(error);
}VLD includes built-in translations for 27+ languages:
import { v, setLocale } from '@oxog/vld';
setLocale('tr'); // Turkish error messages
setLocale('es'); // Spanish
setLocale('de'); // German
setLocale('ja'); // Japanese
setLocale('fr'); // Frenchimport { setLocaleAsync, preloadLocales } from '@oxog/vld/locales/lazy';
// Loads locale on demand via dynamic import()
await setLocaleAsync('tr');
// Preload for SSR / warm start
await preloadLocales(['en', 'de', 'ja']);VLD 3.0 ships the V2 pattern (single-def + check classes) for every chain-heavy
validator. This matches Zod 4.5's "method memoization" optimization and is
3.03x faster than Zod 4.6.4 on the honest head-to-head (10/10 wins,
semantic-checked, see benchmarks/dropin-vs-zod.cjs) and 1.6-10x smaller
in memory.
V3 adds V2 implementations of every leaf and composite validator, exposed as
opt-in factories. The legacy V1 (v.*) remains the default for backwards
compatibility.
| V1 (legacy, default) | V2 (opt-in, faster + smaller) |
|---|---|
v.string() |
v.stringV2() or vV2.string |
v.number() |
v.numberV2() or vV2.number |
v.boolean() |
v.booleanV2() or vV2.boolean |
v.date() |
v.dateV2() |
v.bigint() |
v.bigintV2() |
v.literal(x) |
v.literalV2(x) or vV2.literal(x) |
v.enum([...]) |
v.enumV2([...]) or vV2.enum([...]) |
v.array(item) |
v.arrayV2(item) or vV2.array(item) |
v.union([...]) |
v.unionV2(...) or vV2.union(...) |
v.record(v) |
v.recordV2(v) or vV2.record(v) |
v.string().optional() |
v.optionalV2(v.stringV2()) |
v.string().nullable() |
v.nullableV2(v.stringV2()) |
v.coerce.string() |
v.coerce.stringV2() |
v.refine(s, fn) |
v.refineV2(s, fn) |
v.transform(s, fn) |
v.transformV2(s, fn) |
Option 1 - Drop-in via vV2 (recommended for new code):
// Old (still works, V1):
import { v } from '@oxog/vld';
const schema = v.object({ email: v.string().email() });
// New (V3, drop-in):
import { vV2 as v } from '@oxog/vld'; // just change the import
const schema = v.object({ email: v.string().email() });
// All v.* calls now return V2 internally. v.object() composes them transparently.Option 2 - Global V2 toggle (for existing codebases):
import { v } from '@oxog/vld';
// Switch all v.* factories to V2 once at app start
v.setV2Mode(true);
const s = v.string().email(); // now returns VldStringV2Option 3 - Selective V2 (keep V1 by default, opt in where it matters):
import { v } from '@oxog/vld';
// Keep V1 for simple schemas
const s1 = v.boolean();
// Use V2 for the hot path
const s2 = v.stringV2().min(1).email(); // V2
const s3 = v.arrayV2(v.stringV2()); // V2
const s4 = v.object({ a: v.numberV2().int() }); // mixed - V2 child, V1 object| Schema | V1 (2.4.0) | V2 (3.0) | Zod 4.5 | V2 vs Zod |
|---|---|---|---|---|
string().min(1).email() |
22ms | 22ms | 50ms | 2.3x faster |
number().int().positive().min(1) |
12ms | 6ms | 39ms | 6.5x faster |
object({a:str, b:num}) |
12ms | 11ms | 18ms | 1.6x faster |
| Realistic API (10 fields) | 276ms | 243ms | 767ms | 3.2x faster |
- 28/28 Zod 4.5 parity tests pass.
import { vV2 as z }is a drop-in for Zod 4.5. - Zod 4.6 parity is verified per-release (
npm run verify:zodchecks every publiczod@4.6export - 259 as of 4.6.4) andtests/zod-4-6-parity.test.tscompares behavior against the installed zod. - V1 is the default; existing code works unchanged.
- V2 validators can be children of V1 composites and vice-versa (mixed schemas work).
| Zod 4.6 API | VLD | Notes |
|---|---|---|
schema.validate(data) |
yes | Boolean check, no result object; lazily AOT-compiles on first call - 2.8-33x faster than zod.validate() (node benchmarks/validate-vs-zod.cjs) |
schema.validateAsync(data) |
yes | Async refinements supported |
z.iban() |
yes | ISO 7064 MOD 97-10 checksum in code, pattern inlined for JSON Schema |
z.instanceof(Cls).properties({...}) |
yes | Validates instance fields in place; the prototype survives parsing |
z.withParser(schema, parser) |
yes | Install an externally generated parser (v.INVALID hands back to the runtime) - the CSP-friendly path when new Function is unavailable |
fromJSONSchema new keywords |
yes | minProperties, maxProperties, uniqueItems, contains, minContains, maxContains |
z.emoji() component rejection |
yes | 4.6 regex byte-for-byte; keycaps and flags stay valid |
z.regexes.currencyCode / anyString / iban |
yes | Full 4.6 regex namespace |
tg locale |
yes | Tajik messages |
Each V2 class follows this shape:
- A single
__def: Objectfield on the instance (the only data). - Chain methods create a new
__defviawithDef({...})- no per-instance field shadowing. - Constraints are class instances (
VldCheckMin,VldCheckEmail, ...) with acheck(value): Issue | nullmethod - no per-call payload allocation. isSimpleis precomputed in__deffor the parse hot path.
The pattern is exactly Zod 4.5's "method memoization via prototype getters" but
without the inst[k] = proto[k].bind(inst) trick (which costs a bound function
per method per instance). VLD's V2 trades that for a single shared __def reference
plus a precomputed isSimple boolean.
VLD 3.0 is a non-breaking major bump:
v.*factories still return V1 by default (no existing user code changes).- New
v.*V2andvV2factories are pure additions. - Performance is identical to V2.x for code that doesn't opt in.
- All 2633 existing tests pass; 41 new V2 tests added.
To start using V2 today, just import vV2 instead of v:
import { vV2 as v } from '@oxog/vld';---## Bidirectional Codecs
import { stringToNumber, jsonCodec, base64ToBytes, hexToBytes } from '@oxog/vld';
// String to Number decode & encode
const num = stringToNumber.parse('42'); // 42
const str = stringToNumber.encode(42); // "42"
// JSON codec
const json = jsonCodec();
const parsed = json.parse('{"id":1}');
const encoded = json.encode(parsed);
// Binary conversions
const bytes = base64ToBytes.parse('SGVsbG8=');Functional error handling without exceptions:
import { Ok, Err, match, map, flatMap, tryCatch, isOk, isErr, unwrapOr } from '@oxog/vld';
const result = tryCatch(() => JSON.parse(rawInput));
const output = match(result, {
ok: data => `Success: ${data.id}`,
err: err => `Failed: ${err.message}`,
});import { definePlugin, usePlugin, createVldKernel, v } from '@oxog/vld';
const phonePlugin = definePlugin({
name: 'phone-validator',
version: '1.0.0',
validators: {
phone: () => v.string().regex(/^\+?[1-9]\d{1,14}$/),
},
});
usePlugin(phonePlugin);VLD is optimized for modern V8 runtimes. CI gates enforce performance floors on every commit against Zod:
| Benchmark Case | VLD Throughput | Relative Speedup |
|---|---|---|
| Nullish Parse | ~214M ops/sec | 30.7x faster |
| Number / Positive Int | ~253M ops/sec | 9.1x faster |
| Discriminated Union | ~35M ops/sec | 4.1x faster |
| Optional Parse | ~213M ops/sec | 3.8x faster |
| Union Parse | ~39M ops/sec | 3.3x faster |
| Simple String | ~620M ops/sec | 3.0x faster |
| Array / Object Parse | ~49M ops/sec | 1.7x faster |
Explore full benchmark results and interactive visual comparisons at vld.oxog.dev/benchmark.
npm run benchmark
npm run benchmark:guard
npm run benchmark:memory
npm run benchmark:startup
npm run release:checkContributions are warmly welcome! Please see CONTRIBUTING.md and SECURITY.md for details.
- Documentation & Playground: https://vld.oxog.dev
- NPM Package: https://www.npmjs.com/package/@oxog/vld
- GitHub Repository: https://github.com/ersinkoc/vld
Made with Love by Ersin KOC