Even though my API resembles other solutions at first glance, the implementation and structure of the source code is very different. In the following, I would like to highlight the differences that can be beneficial for both you and your users.

Modular design

Instead of relying on a few large functions with many methods, my API design and source code is based on many small and independent functions, each with just a single task. This modular design has several advantages.

On one hand, my functionality can be easily extended with external code. On the other, it makes my source code more robust and secure because the functionality of the individual functions as well as special edge cases can be tested much easier through unit tests.

However, perhaps the biggest advantage is that a bundler can use the import statements to remove any code that is not needed. Thus, only the code that is actually used ends up in the production build. This allows me to extend my functionality with additional functions without increasing the bundle size for all users.

This can make a big difference, especially for client-side validation, as it reduces the bundle size and, depending on the framework, speeds up the startup time.

import { email, minLength, object, string } from 'valibot'; // 1.06 kB

const LoginSchema = object({
  email: string([
    minLength(1, 'Please enter your email.'),
    email('The email address is badly formatted.'),
  password: string([
    minLength(1, 'Please enter your password.'),
    minLength(8, 'Your password must have 8 characters or more.'),

Comparison with Zod

For example, to validate a simple login form, Zod requires 11.8 kB whereas I requires only 1.06 kB. That's a 91 % reduction in bundle size. This is due to the fact that Zod's functions have several methods with additional functionalities, that cannot be easily removed by current bundlers when they are not executed in your source code.

import { object, string } from 'zod'; // 11.8 kB

const LoginSchema = object({
  email: string()
    .min(1, 'Please enter your email.')
    .email('The email address is badly formatted.'),
  password: string()
    .min(1, 'Please enter your password.')
    .min(8, 'Your password must have 8 characters or more.'),

Bundle size

Besides the individual bundle size, my overall size is also significantly smaller. This is due to the fact that my source code is simpler in structure, less complicated and optimized for compression. To be fair, in the following comparison you must take into account that the functionality between the listed libraries is different and this can have a big impact on the final numbers.

LibraryMinified sizeGzipped sizeCompression rate
Superstruct11.5 kB3.4 kB70.43 %
io-ts20.5 kB5.2 kB74.63 %
Runtypes30.6 kB7.4 kB75.82 %
Valibot46.7 kB7.5 kB83.94 %
Yup40.08 kB12.4 kB69.06 %
Zod57 kB13.2 kB76.84 %
Ajv119.6 kB35.2 kB70.57 %
Joi151.1 kB43 kB71.54 %