If you’ve ever worked on a complex form without a framework, there’s a good chance you’ve used or at least heard of FormValidation. It was the go-to solution for plugin-based, framework-agnostic form validation for years.
Then it went silent. No updates, no responses to issues. Just an archived library slowly rotting against modern JavaScript.
I needed a replacement for a project. I couldn’t find one that matched the flexibility of the plugin model, so I built Validare — a modern TypeScript rewrite of the same architecture, from scratch.
What Validare is
@validare/core is a form validation library with:
-
51 built-in validators — from
notEmptyandemailtoiban,creditCard,uuid,vin, and more - 17 plugins — UI feedback, accessibility, CSS framework integrations, async flows
-
TypeScript-first — full type safety, no
@types/*packages needed - Zero dependencies — no jQuery, no frameworks, no polyfills
- ESM + CJS + UMD — works everywhere
The core idea is the same as FormValidation: a small engine that does nothing on its own, extended entirely through plugins. You add exactly what you need.
Quick start
npm install @validare/core
Enter fullscreen mode Exit fullscreen mode
<form id="myForm" novalidate>
<input type="email" name="email" />
<div class="vd-plugins-message-container"></div>
<button type="submit">Submit</button>
</form>
Enter fullscreen mode Exit fullscreen mode
import { validare, Trigger, Message, SubmitButton } from '@validare/core';
const fv = validare(document.getElementById('myForm'), {
fields: {
email: {
validators: {
notEmpty: { message: 'Email is required' },
email: { message: 'Please enter a valid email address' },
},
},
},
plugins: {
trigger: new Trigger({ event: 'blur' }),
message: new Message(),
submitButton: new SubmitButton(),
},
});
document.getElementById('myForm').addEventListener('submit', async (e) => {
e.preventDefault();
const result = await fv.validate(); // 'Valid' | 'Invalid' | 'NotValidated'
if (result === 'Valid') {
// safe to submit
}
});
Enter fullscreen mode Exit fullscreen mode
That’s it. The Trigger plugin wires up DOM events, Message renders error messages, and SubmitButton disables the button during validation.
The plugin model
Plugins are where Validare earns its keep. Each one is a focused, composable unit:
Plugin What it doesTrigger
Validates on DOM events (blur, input, change)
Message
Renders error messages in the DOM
Icon
Shows ✓ / ✗ icons next to fields
Bootstrap5
Applies is-valid / is-invalid classes
Tailwind
Applies configurable Tailwind utility classes
Aria
Adds aria-invalid and aria-describedby automatically
Sequence
Stops at the first failing validator per field
Excluded
Skips disabled or hidden fields
Declarative
Configures validators via data-vd-* HTML attributes
PasswordStrength
Scores password strength (0–4)
CharCounter
Live character counter with overflow state
Summary
Renders a consolidated error list in a container
Transformer
Normalises field values before validation (trim, strip, etc.)
You compose only what your project needs:
plugins: {
trigger: new Trigger({ event: 'input' }),
message: new Message(),
ui: new Bootstrap5(),
aria: new Aria(),
sequence: new Sequence(),
excluded: new Excluded(),
}
Enter fullscreen mode Exit fullscreen mode
51 validators, organized by category
Core (23): notEmpty, blank, email, creditCard, date, digits, integer, numeric, regexp, uri, identical, different, between, greaterThan, lessThan, stringLength, stringCase, choice, file, callback, promise, remote, ip
Format & Encoding: base64, hex, mac, bic, uuid, color
Financial: iban, vat, cusip, isin, sedol, grid
Publication: ean, isbn, ismn, issn
Device & Vehicle: imei, imo, meid, step, vin
Tax & Business: ein, rtn, siren, siret
Identity & Geographic: id, phone, zipCode
Async validation and remote checks
The remote validator hits an endpoint and expects { valid: boolean }:
username: {
validators: {
notEmpty: { message: 'Username is required' },
remote: {
url: '/api/check-username',
method: 'POST',
message: 'This username is already taken',
},
},
},
Enter fullscreen mode Exit fullscreen mode
Or write your own async validator with callback or promise:
validators: {
callback: {
message: 'Invalid value',
callback: async ({ value }) => {
const res = await fetch(`/api/validate?q=${value}`);
const { valid } = await res.json();
return { valid };
},
},
},
Enter fullscreen mode Exit fullscreen mode
Writing a custom validator
Every validator is a zero-argument factory:
import type { ValidatorFactory } from '@validare/core';
export const noSpaces: ValidatorFactory = () => ({
validate({ value }) {
return { valid: !value.includes(' '), message: 'No spaces allowed' };
},
});
Enter fullscreen mode Exit fullscreen mode
Use it directly in your config — no registration needed:
import { noSpaces } from './validators/noSpaces';
fields: {
username: {
validators: {
notEmpty: { message: 'Required' },
noSpaces: noSpaces,
},
},
},
Enter fullscreen mode Exit fullscreen mode
Coming from FormValidation?
The architecture is intentionally familiar. The main differences:
- Package name:
@validare/core(not@form-validation/cjs) - CSS classes:
vd-plugins-*instead offv-plugins-* - HTML attributes (Declarative plugin):
data-vd-*instead ofdata-fv-* - Native TypeScript — no extra type packages
A full migration guide is available in MIGRATING.md.
Try it
- npm:
npm install @validare/core - Docs: validare.js.org
- GitHub: github.com/validarejs/validare
- CDN:
https://unpkg.com/@validare/core/dist/index.umd.js
Feedback, issues, and PRs are very welcome. This is an active project and I’m happy to prioritize validators or plugins based on what people actually need.
답글 남기기