Search for a command to run...
Last updated June 9, 2026
Ctrovalidate is designed to be extensible. You can author custom synchronous or asynchronous rules via two paths:
Ctrovalidate.addRule() / Ctrovalidate.addAsyncRule() — available to all instancescustomRules option in validation functionsRegister rules globally so they are available to any Ctrovalidate instance across your application.
import { Ctrovalidate } from 'ctrovalidate-browser';
Ctrovalidate.addRule(
'positive',
(value, params, element) => {
const num = parseFloat(value);
return !isNaN(num) && num > 0;
},
'Must be a positive number.'
);<input name="amount" data-ctrovalidate-rules="required|numeric|positive" />Ctrovalidate.addAsyncRule(
'unique',
async (value, params, element, signal) => {
const res = await fetch(`/api/check?v=${value}`, { signal });
const { available } = await res.json();
return available;
},
'This value is already taken.'
);Pass custom rules directly to validation functions. This is the isomorphic approach — works in any JavaScript environment.
import { validateValue } from 'ctrovalidate-core';
const result = validateValue('abc', 'required|startsWithA', {
customRules: {
startsWithA: (value) => String(value).startsWith('a'),
},
});import { validateAsync } from 'ctrovalidate-core';
const results = await validateAsync(data, schema, {
customRules: {
checkDb: async (value, params, ctx, signal) => {
const res = await fetch(`/api/check`, { signal });
return (await res.json()).available;
},
},
});type RuleLogic = (
value: unknown,
params?: unknown[],
element?: HTMLElement | null // browser only; null in core
) => boolean;type AsyncRuleLogic = (
value: unknown,
params?: unknown[],
element?: HTMLElement | null, // browser only; null in core
signal?: AbortSignal
) => Promise<boolean>;Custom rules accept parameters like built-in rules. Parameters are passed as an array of strings (from HTML or string notation) or as-is (from RuleDefinition objects).
// Rule: minAge:18 — params is ['18'] from string, or [18] from object
Ctrovalidate.addRule('minAge', (value, [minAge]) => {
return parseInt(value) >= parseInt(minAge);
}, 'Must be at least {0} years old.');Message interpolation: {0}, {1}, etc., are substituted with the corresponding parameter index.
Group multiple rules into a reusable macro:
import { Ctrovalidate } from 'ctrovalidate-browser';
Ctrovalidate.defineAlias('username', 'required|alphaDash|minLength:3');<input data-ctrovalidate-rules="username" />In the core package, pass aliases via ValidationOptions:
validateValue('john_doe', 'username', {
aliases: { username: 'required|alphaDash|minLength:3' },
});required|numeric|positive).required handle presence. Custom rules should return true for empty values to support optional fields.Ctrovalidate instances.customRules dictionary so they work in both ctrovalidate-core (server) and ctrovalidate-browser (client).