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.
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.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/methods.mdModular schema validation library with best-in-class bundle size (~1KB for basic forms).
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);
}
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()))
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(),
)
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'),
)
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(),
});
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 (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 (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)
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, ' John@Example.COM ');
// Output: 'john@example.com'
try {
const data = v.parse(schema, input);
} catch (error) {
if (error instanceof v.ValiError) {
console.log(error.issues);
}
}
const result = v.safeParse(schema, input);
if (result.success) {
console.log(result.output);
} else {
console.log(result.issues);
}
if (v.is(schema, input)) {
// input is typed
}
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>;
const schema = v.pipe(
v.string(),
v.check((value) => value.includes('@'), 'Must contain @'),
);
const schema = v.pipe(
v.string(),
v.transform((value) => value.split(',').map((s) => s.trim())),
);
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'),
);
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'],
),
);
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()),
});
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);
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>
);
}
| 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 for complete method reference.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.