lite-schema-check
v2.1.0
Published
A tiny, zero-dependency TypeScript validation library. Competitive alternative to Zod with clearer API, solving real pain points like circular validation, codecs, and mixed async/sync validation.
Maintainers
Readme
lite-schema-check 🔥
Zero-dependency TypeScript validation library. A competitive alternative to Zod.
What's New in v2.1 🔥
We implemented the TOP REQUESTED features from Zod's issue tracker:
New in v2.1 (Just Added!)
- ✅ FormData validation (60+ votes) - Zod doesn't have it!
- ✅ Partial validation - Return valid data even with errors
- ✅ JSON Schema export - Better than zod-to-json-schema
- ✅ Schema metadata extraction - Auto-generate forms
- ✅ XOR (exclusive OR) - Cleaner than Zod's approach
- ✅ File validation - Size & MIME type checking
- ✅ Schema descriptions - Rich metadata support
From v2.0 (Already Have It!)
- ✅ Circular/recursive validation (Zod Issue #5346) - They don't have it yet
- ✅ Clearer optional/nullable API (Zod Issue #5348) - Less confusing than Zod
- ✅ Mixed sync/async validation (Zod Issue #5379) - Selective async support
- ✅ Reversible transforms (Codecs) (Zod Issues #5374, #5377) - Encode AND decode
That's 11 features Zod doesn't have!
Quick Comparison
| Feature | Zod | lite-schema-check v2.1 | |---------|-----|----------------------| | Basic validation | ✅ | ✅ | | Nested objects | ✅ | ✅ | | Optional/nullable | ⚠️ (confusing) | ✅ Clearer | | FormData validation | ❌ | ✅ We have it 🔥 | | Partial validation | ❌ | ✅ We have it 🔥 | | JSON Schema export | ⚠️ (external) | ✅ Built-in 🔥 | | Schema metadata | ⚠️ (hard) | ✅ Easy 🔥 | | XOR support | ⚠️ (clunky) | ✅ Clean 🔥 | | Circular validation | ❌ | ✅ We have it | | Mixed async/sync | ❌ | ✅ We have it | | Reversible transforms | ❌ | ✅ We have it | | Bundle size | ~2KB | ~3KB minified | | Battle-tested | ✅ Years | ⚠️ Growing |
Installation
npm install lite-schema-checkQuick Start
🔥 FormData Validation (NEW!)
import { validateFormData, object, string, number, file } from 'lite-schema-check/v2';
const schema = object({
name: string.min(3),
age: number.positive(),
avatar: file({ maxSize: 5_000_000, mimeTypes: ['image/png', 'image/jpeg'] })
});
// In Remix/Next.js server action:
export async function action({ request }) {
const formData = await request.formData();
const result = validateFormData(formData, schema);
if (result.success) {
// Automatic type conversion! age is number, not string
await createUser(result.data);
}
}🔄 Partial Validation (NEW!)
import { validatePartial, object, string, number } from 'lite-schema-check/v2';
const schema = object({
name: string.min(3),
email: string.email(),
age: number.positive()
});
const data = {
name: 'John', // ✅ Valid
email: 'not-email', // ❌ Invalid
age: 30 // ✅ Valid
};
const result = validatePartial(data, schema);
// Save what's valid, show errors for what's not
await saveDraft(result.validData); // { name: 'John', age: 30 }
showErrors(result.invalidFields); // { email: {...} }📄 JSON Schema Export (NEW!)
import { toJSONSchema, describe, optional } from 'lite-schema-check/v2';
const schema = object({
name: describe(string.min(3), 'User full name'),
age: optional(number.positive())
});
const jsonSchema = toJSONSchema(schema);
// {
// type: 'object',
// properties: {...},
// required: ['name'] // ✅ 'age' correctly NOT required!
// }Basic Validation
import { validate, object, optional, array, union, literal } from 'lite-schema-check/v2';
// Define a schema with all the features
const userSchema = object({
name: 'string',
age: 'number',
email: optional('string'), // Can be missing
tags: array('string', { min: 1, max: 10 }),
role: union(
literal('admin'),
literal('user'),
literal('guest')
),
profile: object({
bio: 'string',
website: optional('string')
})
});
// Validate data
const result = validate(userData, userSchema);
if (result.success) {
console.log('Valid!', result.data);
} else {
console.log('Errors:', result.errors);
// Detailed error paths: ['profile', 'bio']
}V1 API (Still works)
import { validate } from 'lite-schema-check';
const schema = { name: 'string', age: 'number' };
const result = validate({ name: 'Alice', age: 30 }, schema);
// { isValid: true, errors: [] }Why Choose Us Over Zod?
1. 🔥 FormData Validation (60+ Votes!)
Problem: Zod has NO FormData support. Every Remix/Next.js dev needs this.
Our Solution:
import { validateFormData, object, string, number, file } from 'lite-schema-check/v2';
const schema = object({
name: string.min(3),
age: number.positive(),
avatar: file({ maxSize: 5_000_000, mimeTypes: ['image/png'] })
});
// Automatic type conversion: strings → numbers, File validation
const result = validateFormData(formData, schema);Status: ✅ We have it | ❌ Zod doesn't
2. 🔥 Partial Validation
Problem: Zod returns nothing if ANY field fails. Can't save partial data.
Our Solution:
import { validatePartial } from 'lite-schema-check/v2';
const result = validatePartial(data, schema);
// Returns:
// {
// validData: { name: 'John', age: 30 }, // Save these!
// invalidFields: { email: {...} } // Show errors
// }
await saveDraft(result.validData); // Save what worksUse Cases: Auto-save forms, progressive validation, data migration
Status: ✅ We have it | ❌ Zod doesn't
3. 🔥 JSON Schema Export (Better than zod-to-json-schema)
Problem: Zod needs external package with bugs (optional fields marked required)
Our Solution:
import { toJSONSchema, toOpenAPISchema, describe } from 'lite-schema-check/v2';
const schema = object({
name: describe(string.min(3), 'User full name'),
age: optional(number.positive())
});
const jsonSchema = toJSONSchema(schema);
// ✅ Optional fields correctly NOT in required array
// ✅ Descriptions preserved
// ✅ OpenAPI 3.0 compatibleStatus: ✅ Built-in | ⚠️ Zod needs external package
4. ✅ Circular/Recursive Validation (Zod Issue #5346)
Problem: Zod users struggle with tree structures, categories, comment threads.
Our Solution:
import { object, array, lazy } from 'lite-schema-check/v2';
const categorySchema = object({
name: 'string',
children: array(lazy(() => categorySchema))
});
// Validates infinitely nested categories!Status: ✅ We have it | ❌ Zod doesn't (issue open since Oct 16, 2025)
2. ✅ Clearer Optional/Nullable API (Zod Issue #5348)
Problem: Zod's .required() on .optional() confuses users.
Our Solution:
import { object, optional, nullable, nullish } from 'lite-schema-check/v2';
const schema = object({
email: optional('string'), // Can be missing entirely
phone: nullable('string'), // Can be null (must be present)
bio: nullish('string') // Can be null OR undefined
});Status: ✅ Clearer than Zod's API
3. ✅ Mixed Sync/Async Validation (Zod Issue #5379)
Problem: Zod is all-sync or all-async, no mixing.
Our Solution:
import { object, async } from 'lite-schema-check/v2';
import { string } from 'lite-schema-check/presets';
const schema = object({
username: async('string', async (val) => {
// Check database
const exists = await checkUsernameExists(val);
if (exists) throw new Error('Username taken');
}),
email: string.email() // Sync validation
});
// Validates efficientlyStatus: ✅ We support selective async | ❌ Zod doesn't
4. ✅ Reversible Transforms (Codecs) (Zod Issues #5374, #5377)
Problem: Zod transforms are one-way. Users want to serialize back.
Our Solution:
import { codec } from 'lite-schema-check/v2';
const dateCodec = codec(
'string',
(str) => new Date(str), // Parse (decode)
(date) => date.toISOString() // Serialize (encode)
);
// Can parse API responses AND serialize back!Status: ✅ Bidirectional | ❌ Zod is one-way only
Feature Showcase
Arrays with Constraints
import { object, array } from 'lite-schema-check/v2';
const schema = object({
tags: array('string', { min: 1, max: 5 })
});
validate({ tags: ['js', 'ts'] }); // ✅ Valid
validate({ tags: [] }); // ❌ Too few
validate({ tags: ['a','b','c','d','e','f'] }); // ❌ Too manyNested Objects
const orderSchema = object({
customer: object({
name: 'string',
address: object({
street: 'string',
city: 'string',
zip: 'number'
})
}),
items: array(object({
product: 'string',
quantity: 'number'
}))
});
// Validates deeply nested structuresUnions & Literals (Enums)
const taskSchema = object({
status: union(
literal('todo'),
literal('in_progress'),
literal('done')
),
priority: union(literal(1), literal(2), literal(3))
});
// Type-safe enum-like validationString Refinements
import { string } from 'lite-schema-check/presets';
const schema = object({
email: string.email(),
url: string.url(),
uuid: string.uuid(),
password: string.min(8),
username: string.regex(/^[a-z0-9_]+$/)
});Number Refinements
import { number } from 'lite-schema-check/presets';
const schema = object({
age: number.positive(),
rating: number.int(),
price: number.min(0),
discount: number.max(100)
});API Reference
Core Functions
validate(input: unknown, schema: Schema): ValidationResult
Validates data against a schema.
assertValid(input: unknown, schema: Schema): asserts input
Throws if validation fails.
createValidator(schema: Schema): (input: unknown) => ValidationResult
Creates a reusable validator.
Schema Builders
object(shape)- Define object schemaoptional(schema)- Mark field as optionalnullable(schema)- Mark field as nullablenullish(schema)- Mark field as nullable OR optionalarray(items, constraints?)- Define array with optional min/maxunion(...options)- Multiple type optionsliteral(value)- Exact value match
Advanced Features
lazy(factory)- Circular/recursive schemasasync(schema, validator)- Async validationcodec(schema, decode, encode)- Reversible transformsrefine(schema, refiner)- Custom validationtransform(schema, transformer)- Data transformation
TypeScript Support
Full TypeScript support with type inference:
import { validate, object, ValidationResult } from 'lite-schema-check/v2';
const schema = object({
name: 'string',
age: 'number'
});
const result: ValidationResult = validate(data, schema);
if (result.success) {
// result.data is typed!
}When to Use lite-schema-check
✅ Use Us When:
- 🔥 You're using Remix / Next.js - FormData validation built-in
- 🔥 You need auto-save forms - Partial validation
- 🔥 You need API docs - JSON Schema / OpenAPI export
- 🔥 You're building forms - Schema metadata extraction
- You need circular/recursive validation
- You want clearer optional/nullable API
- You need reversible transforms (codecs)
- You need mixed sync/async validation
- Bundle size matters (edge functions)
- You prefer simpler, more predictable API
⚠️ Use Zod When:
- You need battle-tested production stability
- You need the richest ecosystem
- You need advanced TypeScript type inference
- You're already using it (migration cost)
- You don't need the features we have
Migration from Zod
Most Zod schemas translate directly:
// Zod
z.object({
name: z.string(),
age: z.number(),
email: z.string().optional()
})
// lite-schema-check
object({
name: 'string',
age: 'number',
email: optional('string')
})Most schemas migrate directly with minimal changes.
Documentation
- Contributing Guide - How to contribute
- Code of Conduct - Community guidelines
- Security Policy - Security vulnerability reporting
- Changelog - Version history
Examples
Check the /examples folder:
- 🔥
formdata-validation.ts- NEW! 5 FormData examples (Remix/Next.js) - 🔥
partial-validation.ts- NEW! 5 partial validation examples (auto-save) - 🔥
json-schema-metadata.ts- NEW! 6 JSON Schema examples (OpenAPI) v2-vs-zod.ts- Feature comparisonzod-pain-points.ts- Solutions to Zod issues
Roadmap
✅ Completed (v2.1)
- [x] Basic validation
- [x] Optional/nullable fields
- [x] Nested objects
- [x] Arrays with constraints
- [x] Unions & literals
- [x] String/number refinements
- [x] Circular validation (lazy)
- [x] Async validation
- [x] Codecs (reversible transforms)
- [x] 🔥 FormData validation
- [x] 🔥 Partial validation
- [x] 🔥 JSON Schema export
- [x] 🔥 Schema metadata
- [x] 🔥 XOR support
- [x] 🔥 File validation
- [x] 🔥 Schema descriptions
📋 Coming Soon
- [ ] Schema composition (extend, merge, pick, omit)
- [ ] Discriminated unions
- [ ] Better error messages
- [ ] Performance optimization
- [ ] Framework integrations (Remix, Next.js plugins)
Contributing
Contributions welcome! We're actively building features Zod users are asking for.
How to contribute:
- Watch Zod's issues
- Implement requested features
- Submit PR with tests
- Help us compete!
License
MIT
Acknowledgments
Made with ❤️ by developers frustrated with existing validation libraries.
Give us a try! We're actively solving problems Zod users are asking for. ⭐
