mgd34msu/goodvibes-gemini

valibot

Validates data with Valibot's modular, tree-shakable schema library for minimal bundle size. Use when bundle size matters, building form validation, or needing lightweight TypeScript validation.

First seen Mar 29, 2026

Installation

$ npx skills add mgd34msu/goodvibes-gemini --skill valibot

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from mgd34msu/goodvibes-gemini · top by installs.

npx skills add mgd34msu/goodvibes-gemini

Browse all from mgd34msu/goodvibes-gemini

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Also listed on

Alternate registries and mirrors of this skill.

Repository health

Stars 4
License MIT
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,558 B
  • docs SUMMARY.md 209 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 3 installs

SKILL.md

Valibot

Modular schema validation library with best-in-class bundle size (~1KB for basic forms).

Quick Start

npm install valibot
import * as v from 'valibot';

const schema = v.object({
  name: v.pipe(v.string(), v.minLength(2)),
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.minValue(0)),
});

// Validate
const result = v.safeParse(schema, data);

if (result.success) {
  console.log(result.output);
} else {
  console.log(result.issues);
}

Schema Types

Primitives

import * as v from 'valibot';

// String
v.string()
v.pipe(v.string(), v.minLength(1), v.maxLength(100))

// Number
v.number()
v.pipe(v.number(), v.minValue(0), v.maxValue(100))

// Boolean
v.boolean()

// Null/Undefined
v.null_()
v.undefined_()

// BigInt
v.bigint()

// Symbol
v.symbol()

// Date
v.date()
v.pipe(v.date(), v.minValue(new Date()))

String Validations

v.pipe(
  v.string(),
  v.minLength(1, 'Required'),
  v.maxLength(100, 'Too long'),
  v.email('Invalid email'),
  v.url('Invalid URL'),
  v.uuid('Invalid UUID'),
  v.regex(/^[a-z]+$/, 'Only lowercase'),
  v.startsWith('https://'),
  v.endsWith('.com'),
  v.includes('@'),
  v.trim(),
  v.toLowerCase(),
  v.toUpperCase(),
)

Number Validations

v.pipe(
  v.number(),
  v.minValue(0, 'Must be positive'),
  v.maxValue(100, 'Max 100'),
  v.integer('Must be integer'),
  v.multipleOf(5, 'Must be multiple of 5'),
  v.finite('Must be finite'),
  v.safeInteger('Must be safe integer'),
)

Object

const userSchema = v.object({
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
  age: v.optional(v.number()),
});

// Strict (no extra keys)
const strictSchema = v.strictObject({
  id: v.number(),
  name: v.string(),
});

// Loose (allow extra keys)
const looseSchema = v.looseObject({
  id: v.number(),
});

Array

v.array(v.string())
v.pipe(
  v.array(v.string()),
  v.minLength(1, 'At least one item'),
  v.maxLength(10, 'Max 10 items'),
)

// Tuple
v.tuple([v.string(), v.number()])

Union & Intersection

// Union (OR)
const statusSchema = v.union([
  v.literal('active'),
  v.literal('inactive'),
  v.literal('pending'),
]);

// Variant (discriminated union)
const eventSchema = v.variant('type', [
  v.object({ type: v.literal('click'), x: v.number(), y: v.number() }),
  v.object({ type: v.literal('scroll'), offset: v.number() }),
]);

// Intersection (AND)
const combined = v.intersect([
  v.object({ id: v.number() }),
  v.object({ name: v.string() }),
]);

Optional & Nullable

// Optional (undefined allowed)
v.optional(v.string())

// Nullable (null allowed)
v.nullable(v.string())

// Nullish (null or undefined)
v.nullish(v.string())

// With default
v.optional(v.string(), 'default value')
v.nullable(v.number(), 0)

Pipe System

Valibot uses pipe() to chain validations and transformations:

const schema = v.pipe(
  v.string(),
  v.trim(),              // Transform
  v.minLength(1),        // Validate
  v.toLowerCase(),       // Transform
  v.email(),             // Validate
);

// Order matters!
const result = v.parse(schema, '  [email protected]  ');
// Output: '[email protected]'

Validation

parse() - Throws on Error

try {
  const data = v.parse(schema, input);
} catch (error) {
  if (error instanceof v.ValiError) {
    console.log(error.issues);
  }
}

safeParse() - Returns Result

const result = v.safeParse(schema, input);

if (result.success) {
  console.log(result.output);
} else {
  console.log(result.issues);
}

is() - Type Guard

if (v.is(schema, input)) {
  // input is typed
}

Type Inference

import * as v from 'valibot';

const userSchema = v.object({
  id: v.number(),
  name: v.string(),
  email: v.pipe(v.string(), v.email()),
  roles: v.array(v.string()),
});

// Infer TypeScript type
type User = v.InferOutput<typeof userSchema>;
// { id: number; name: string; email: string; roles: string[] }

// Infer input type (before transforms)
type UserInput = v.InferInput<typeof userSchema>;

Custom Validation

check() - Custom Predicate

const schema = v.pipe(
  v.string(),
  v.check((value) => value.includes('@'), 'Must contain @'),
);

transform() - Custom Transform

const schema = v.pipe(
  v.string(),
  v.transform((value) => value.split(',').map((s) => s.trim())),
);

Custom Schema

const passwordSchema = v.pipe(
  v.string(),
  v.minLength(8, 'At least 8 characters'),
  v.regex(/[A-Z]/, 'Need uppercase'),
  v.regex(/[a-z]/, 'Need lowercase'),
  v.regex(/[0-9]/, 'Need number'),
);

Common Patterns

User Registration

const registrationSchema = v.object({
  email: v.pipe(
    v.string(),
    v.email('Invalid email'),
  ),
  password: v.pipe(
    v.string(),
    v.minLength(8, 'At least 8 characters'),
    v.regex(/[A-Z]/, 'Need uppercase'),
    v.regex(/[0-9]/, 'Need number'),
  ),
  confirmPassword: v.string(),
  age: v.pipe(
    v.number(),
    v.minValue(18, 'Must be 18+'),
  ),
  terms: v.pipe(
    v.boolean(),
    v.value(true, 'Must accept terms'),
  ),
});

// Add cross-field validation
const fullSchema = v.pipe(
  registrationSchema,
  v.forward(
    v.partialCheck(
      [['password'], ['confirmPassword']],
      (input) => input.password === input.confirmPassword,
      'Passwords must match',
    ),
    ['confirmPassword'],
  ),
);

API Response

const apiResponseSchema = v.object({
  success: v.boolean(),
  data: v.optional(v.object({
    users: v.array(v.object({
      id: v.number(),
      name: v.string(),
    })),
  })),
  error: v.optional(v.string()),
});

Environment Variables

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

const env = v.parse(envSchema, process.env);

React Hook Form Integration

import { useForm } from 'react-hook-form';
import { valibotResolver } from '@hookform/resolvers/valibot';
import * as v from 'valibot';

const schema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

type FormData = v.InferOutput<typeof schema>;

function Form() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: valibotResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input {...register('password')} type="password" />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

Bundle Size Comparison

Library Basic Form
Valibot ~1.4 KB
Zod ~12 KB
Yup ~15 KB

Valibot achieves this through modular imports - only used functions are bundled.

See [references/methods.md](references/methods.md) for complete method reference.