@cleverbrush/schema vs Zod
Zod is the industry standard for TypeScript schema validation — battle-tested, widely supported, and API-compatible with most ecosystem tooling. @cleverbrush/schema was designed with Zod as a reference point: the primitives and fluent builder style are intentionally familiar so migration is incremental.
Where they differ is in areas Zod never tackled: typed field-error selectors, runtime schema introspection, and a first-class extension system.
Key differences at a glance
| Capability | @cleverbrush/schema | Zod 3/4 | Notes |
|---|---|---|---|
| TypeScript type inference | ✓ | ✓ | InferType<> vs z.infer<> |
| Fluent / chainable API | ✓ | ✓ | Nearly identical call sites |
| Immutable schemas | ✓ | ✓ | Both return a new instance on every method |
| Zero runtime dependencies | ✓ | ✓ | Both have no external deps |
| Bundle size (gzipped full) | ~15 KB | ~14 KB (v3) / ~6 KB (v4) | Tree-shakeable — single builder entry points as small as ~4 KB |
| Validation speed (array of 100 objects) | 34 K ops/s | 17 K ops/s | ~2× faster in benchmarks |
| Standard Schema v1 | ✓ | ✓ | Both work with TanStack Form, T3 Env, etc. |
| Custom error messages | ✓ | ✓ | Identical capability |
| Default values | ✓ | ✓ | Identical: .default(value) or factory fn |
| Branded types | ✓ | ✓ | Identical API |
| Readonly schemas | ✓ | ✓ | Identical API |
| Typed field-error selectors | ✓ | ✗ | result.getErrorsFor(u => u.field) — typos caught at compile time |
| Runtime schema introspection | ✓ | ~ | .introspect() returns a typed stable descriptor tree; Zod exposes internals via ._def / ._zod.def (undocumented, unstable API) |
| PropertyDescriptors / metadata | ✓ | ✗ | Per-field labels, descriptions, metadata |
| JSDoc comment preservation | ✓ | ✗ | Carry JSDoc through to form labels and JSON Schema |
| Type-safe extension system | ✓ | limited | Add typed methods to builders; Zod only has .refine() |
| extern() — Standard Schema interop | ✓ | ✗ | Wrap any Standard Schema v1 library (incl. Zod) as a property |
| Per-property error inspection | ✓ | path-based | Zod uses string/index paths; cleverbrush uses typed lambda selectors |
| Sync + async validation | ✓ | ✓ | Identical capability |
z.tuple([...]) | ✓ | ✓ | Use tuple([...]) |
z.record(key, value) | ✓ | ✓ | Use record(string(), value) |
z.map() / z.set() | ✓ | ✓ | Use any().hasType(Map) / any().hasType(Set) with preprocessors or custom validators |
Comparison with other validation libraries
How does @cleverbrush/schema compare to broader alternatives?
| Feature | @cleverbrush/schema | Zod | Yup | Joi |
|---|---|---|---|---|
| TypeScript type inference | ✓ | ✓ | ~ | ✗ |
| Immutable schemas | ✓ | ✓ | ✗ | ✗ |
| Zero dependencies | ✓ | ✓ | ✗ | ✗ |
| Runtime introspection | ✓ | ~ | ✗ | ~ |
| PropertyDescriptors | ✓ | ✗ | ✗ | ~ |
| JSDoc comments preservation | ✓ | ✗ | ✗ | ✗ |
| Custom error messages | ✓ | ✓ | ✓ | ✓ |
| Per-property error inspection | ✓ | ~ | ~ | ~ |
| Sync + async validation | ✓ | ✓ | ✓ | ✓ |
| Standard Schema v1 | ✓ | ✓ | ✗ | ✗ |
| Extension / plugin system | ✓ | ~ | ✗ | ~ |
External schema interop (extern()) | ✓ | ✗ | ✗ | ✗ |
| Built-in validators (email…) | ✓ | ✓ | ✓ | ✓ |
| Default values | ✓ | ✓ | ✓ | ✓ |