Data Schema Overview
The data-schema package is a synchronous validation library with full TypeScript type inference. It implements Standard Schema v1, making it compatible with any library that supports the standard. Think of it as a lightweight alternative to Zod or Valibot that ships with Remix.
What is Standard Schema v1?
Standard Schema is an open specification that defines a common interface for schema validation libraries. Any tool that supports Standard Schema can use data-schema schemas without an adapter. This includes form libraries, API frameworks, and other validation tools.
Key Features
| Feature | What It Does |
|---|---|
| Sync-first | parse() and parseSafe() run synchronously. No await needed. |
| Full type inference | TypeScript infers the output type from your schema definition. |
| Primitives | string(), number(), boolean(), bigint(), symbol(), null_(), undefined_(), any(). |
| Composites | object(), array(), tuple(), record(), map(), set(), union(), variant(). |
| Checks | email(), url(), min(), max(), minLength(), maxLength() via .pipe(). |
| Coercions | Parse strings from form data or URL params into numbers, booleans, dates. |
| FormData parsing | Validate FormData and URLSearchParams directly with remix/data-schema/form-data. |
| Standard Schema v1 | Every schema satisfies the Standard Schema interface. |
Quick Example
Validate a user object:
import { object, string, number, optional, parse } from 'remix/data-schema'
import { email, minLength, min, max } from 'remix/data-schema/checks'
let userSchema = object({
name: string().pipe(minLength(1)),
email: string().pipe(email()),
age: number().pipe(min(0), max(150)),
bio: optional(string()),
})
// Throws ValidationError if invalid
let user = parse(userSchema, {
name: 'Alice',
email: 'alice@example.com',
age: 30,
})
// user: { name: string; email: string; age: number; bio: string | undefined }TypeScript infers the output type automatically --- no need for a separate type definition.
parse vs. parseSafe
parse() throws a ValidationError on failure. parseSafe() returns a discriminated result:
import { parseSafe, string } from 'remix/data-schema'
let result = parseSafe(string(), 42)
if (result.success) {
console.log(result.value) // the valid string
} else {
console.log(result.issues) // array of { message, path }
}When to use which?
Use parse() when invalid input is an error you want to catch (API handlers, server actions). Use parseSafe() when you need to display validation errors to the user (form validation).
Checks with .pipe()
Checks are reusable validation rules that you compose onto a schema with .pipe():
import { string } from 'remix/data-schema'
import { email, minLength, maxLength } from 'remix/data-schema/checks'
let emailSchema = string().pipe(email())
let usernameSchema = string().pipe(minLength(3), maxLength(20))Coercions for Form Data
Form fields are always strings. Use coercions to convert them:
import * as coerce from 'remix/data-schema/coerce'
import { object, parse } from 'remix/data-schema'
let querySchema = object({
page: coerce.number(),
active: coerce.boolean(),
})
parse(querySchema, { page: '3', active: 'true' })
// { page: 3, active: true }Imports at a Glance
// Core types and parsing
import { object, string, number, boolean, array, parse, parseSafe } from 'remix/data-schema'
// Validation checks
import { email, minLength, min, max } from 'remix/data-schema/checks'
// Coercions (string -> number, boolean, date)
import * as coerce from 'remix/data-schema/coerce'
// FormData parsing
import * as fd from 'remix/data-schema/form-data'Related
- Data Schema Tutorial --- Validate user registration, form data, and API input step by step.
- Data Schema Reference --- Full API reference for every export.
- Validation Guide --- Practical validation patterns.
- Forms & Mutations --- Form handling with schema validation.