DEV Community

Alex Spinov
Alex Spinov

Posted on

Valibot Has a Free Schema Validation Library — Here's How to Use It

Zod is great but ships 57KB to your users' browsers. Valibot does the same thing at under 1KB — up to 98% smaller bundle size with the same developer experience.

What Is Valibot?

Valibot is a TypeScript-first schema validation library. It uses a modular, tree-shakeable architecture that means you only ship the validators you actually use.

Bundle Size Comparison

Library Bundle Size Tree-Shakeable
Valibot ~0.5-1KB Yes (modular)
Zod ~57KB No (monolithic)
Yup ~40KB Partial
Joi ~150KB No

Quick Start

npm install valibot
Enter fullscreen mode Exit fullscreen mode
import * as v from 'valibot';

const UserSchema = v.object({
  name: v.pipe(v.string(), v.minLength(2), v.maxLength(100)),
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.minValue(0), v.maxValue(150)),
  role: v.picklist(['admin', 'user', 'moderator']),
});

type User = v.InferOutput<typeof UserSchema>;
// { name: string; email: string; age: number; role: 'admin' | 'user' | 'moderator' }

// Validate
const result = v.safeParse(UserSchema, inputData);
if (result.success) {
  console.log(result.output); // Typed as User
} else {
  console.log(result.issues); // Detailed error messages
}
Enter fullscreen mode Exit fullscreen mode

Common Patterns

API Response Validation

const ApiResponseSchema = v.object({
  data: v.array(v.object({
    id: v.number(),
    title: v.string(),
    tags: v.array(v.string()),
    metadata: v.optional(v.record(v.string(), v.unknown())),
  })),
  pagination: v.object({
    page: v.number(),
    totalPages: v.number(),
    hasNext: v.boolean(),
  }),
});

const response = await fetch('/api/articles');
const json = await response.json();
const { output } = v.parse(ApiResponseSchema, json);
// output is fully typed
Enter fullscreen mode Exit fullscreen mode

Form Validation

const LoginSchema = v.object({
  email: v.pipe(
    v.string(),
    v.email('Please enter a valid email'),
  ),
  password: v.pipe(
    v.string(),
    v.minLength(8, 'Password must be at least 8 characters'),
    v.regex(/[A-Z]/, 'Must contain uppercase letter'),
    v.regex(/[0-9]/, 'Must contain a number'),
  ),
});

// With React Hook Form
const form = useForm({
  resolver: valibotResolver(LoginSchema),
});
Enter fullscreen mode Exit fullscreen mode

Environment Variables

const EnvSchema = v.object({
  DATABASE_URL: v.pipe(v.string(), v.url()),
  API_KEY: v.pipe(v.string(), v.minLength(32)),
  PORT: v.pipe(v.string(), v.transform(Number), v.number()),
  NODE_ENV: v.picklist(['development', 'production', 'test']),
});

const env = v.parse(EnvSchema, process.env);
Enter fullscreen mode Exit fullscreen mode

Why Valibot Over Zod

The API is nearly identical — switching is straightforward:

// Zod
const schema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
});

// Valibot — same logic, 98% smaller
const schema = v.object({
  name: v.pipe(v.string(), v.minLength(2), v.maxLength(100)),
  email: v.pipe(v.string(), v.email()),
});
Enter fullscreen mode Exit fullscreen mode

Framework Integration

  • React Hook Form@hookform/resolvers
  • SvelteKitsveltekit-superforms
  • Nuxt — direct integration
  • tRPC — input validation
  • Hono — request validation middleware

Get Started


Validating scraped data? My Apify actors extract clean, structured data — combine with Valibot for bulletproof pipelines. Custom solutions: spinov001@gmail.com

Top comments (0)