FormValidation is discontinued — here's the TypeScript successor I built
FormValidation is discontinued — here’s the TypeScript successor I built
FormValidation 已停止维护 — 这是我构建的 TypeScript 继任者
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. 如果你曾经在没有框架的情况下处理过复杂的表单,那么你很可能使用过或至少听说过 FormValidation。多年来,它一直是基于插件、与框架无关的表单验证的首选解决方案。后来它沉寂了。没有更新,对 Issue 也没有回应。它只是一个被归档的库,在现代 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. 我为一个项目寻找替代品,但找不到能与那种插件模型灵活性相媲美的方案,所以我构建了 Validare —— 一个从零开始、基于相同架构的现代 TypeScript 重写版本。
What Validare is
Validare 是什么
@validare/core is a form validation library with: @validare/core 是一个表单验证库,具备以下特性:
- 51 built-in validators — from notEmpty and email to iban, creditCard, uuid, vin, and more
- 51 个内置验证器 — 从 notEmpty 和 email 到 iban、creditCard、uuid、vin 等等
- 17 plugins — UI feedback, accessibility, CSS framework integrations, async flows
- 17 个插件 — UI 反馈、无障碍支持、CSS 框架集成、异步流程
- TypeScript-first — full type safety, no @types/* packages needed
- TypeScript 优先 — 完全的类型安全,无需安装 @types/* 包
- Zero dependencies — no jQuery, no frameworks, no polyfills
- 零依赖 — 无需 jQuery、框架或 polyfills
- ESM + CJS + UMD — works everywhere
- ESM + CJS + UMD — 随处可用
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. 其核心理念与 FormValidation 相同:一个本身什么都不做的小型引擎,完全通过插件进行扩展。你只需添加你真正需要的功能。
Quick start
快速开始
npm install @validare/core
<form id="myForm" novalidate>
<input type="email" name="email" />
<div class="vd-plugins-message-container"></div>
<button type="submit">Submit</button>
</form>
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
}
});
That’s it. The Trigger plugin wires up DOM events, Message renders error messages, and SubmitButton disables the button during validation. 就是这样。Trigger 插件负责连接 DOM 事件,Message 负责渲染错误信息,而 SubmitButton 则在验证期间禁用按钮。
The plugin model
插件模型
Plugins are where Validare earns its keep. Each one is a focused, composable unit: 插件是 Validare 的核心价值所在。每一个插件都是一个专注且可组合的单元:
| Plugin | What it does |
|---|---|
| 插件 | 功能描述 |
| Trigger | Validates on DOM events (blur, input, change) |
| Trigger | 在 DOM 事件(blur, input, change)时进行验证 |
| Message | Renders error messages in the DOM |
| Message | 在 DOM 中渲染错误信息 |
| Icon | Shows ✓ / ✗ icons next to fields |
| Icon | 在字段旁边显示 ✓ / ✗ 图标 |
| Bootstrap5 | Applies is-valid / is-invalid classes |
| Bootstrap5 | 应用 is-valid / is-invalid 类名 |
| Tailwind | Applies configurable Tailwind utility classes |
| Tailwind | 应用可配置的 Tailwind 工具类 |
| Aria | Adds aria-invalid and aria-describedby automatically |
| Aria | 自动添加 aria-invalid 和 aria-describedby 属性 |
| Sequence | Stops at the first failing validator per field |
| Sequence | 在每个字段的第一个验证失败处停止 |
| Excluded | Skips disabled or hidden fields |
| Excluded | 跳过被禁用或隐藏的字段 |
| Declarative | Configures validators via data-vd-* HTML attributes |
| Declarative | 通过 data-vd-* HTML 属性配置验证器 |
| PasswordStrength | Scores password strength (0–4) |
| PasswordStrength | 评估密码强度 (0–4) |
| CharCounter | Live character counter with overflow state |
| CharCounter | 带有溢出状态的实时字符计数器 |
| Summary | Renders a consolidated error list in a container |
| Summary | 在容器中渲染汇总的错误列表 |
| Transformer | Normalises field values before validation (trim, strip, etc.) |
| Transformer | 在验证前规范化字段值(trim, strip 等) |
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(),
}
51 validators, organized by category
51 个验证器,按类别组织
- 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
- 核心 (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
- 格式与编码: base64, hex, mac, bic, uuid, color
- Financial: iban, vat, cusip, isin, sedol, grid
- 金融: iban, vat, cusip, isin, sedol, grid
- Publication: ean, isbn, ismn, issn
- 出版: ean, isbn, ismn, issn
- Device & Vehicle: imei, imo, meid, step, vin
- 设备与车辆: imei, imo, meid, step, vin
- Tax & Business: ein, rtn, siren, siret
- 税务与商业: ein, rtn, siren, siret
- Identity & Geographic: id, phone, zipCode
- 身份与地理: id, phone, zipCode
Async validation and remote checks
异步验证与远程检查
The remote validator hits an endpoint and expects { valid: boolean }:
remote 验证器会请求一个端点,并期望返回 { valid: boolean }:
username: {
validators: {
notEmpty: { message: 'Username is required' },
remote: {
url: '/api/check-username',
method: 'POST',
message: 'This username is already taken',
},
},
},
Or write your own async validator with callback or promise: 或者使用 callback 或 promise 编写你自己的异步验证器:
validators: {
callback: {
message: 'Invalid value',
callback: async ({ value }) => {
const res = await fetch(`/api/validate?q=${value}`);
const { valid } = await res.json();
return { valid };
},
},
},
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' };
},
});
Use it directly in your config — no registration needed: 直接在配置中使用它 —— 无需注册:
import { noSpaces } from './validators/noSpaces';
fields: {
username: {
validators: {
notEmpty: { message: 'Required' },
noSpaces: noSpaces,
},
},
},
Coming from FormValidation?
从 FormValidation 迁移而来?
The architecture is intentionally familiar. The main differences: 其架构特意保持了熟悉感。主要区别如下:
- Package name: @validare/core (not @form-validation/cjs)
- 包名: @validare/core (而非 @form-validation/cjs)
- CSS classes: vd-plugins-* instead of fv-plugins-*
- CSS 类名: vd-plugins-* (而非 fv-plugins-*)
- HTML attributes (Declarative plugin): data-vd-* instead of data-fv-*
- HTML 属性 (Declarative 插件): data-vd-* (而非 data-fv-*)
- Native TypeScript — no extra type packages
- 原生 TypeScript — 无需额外的类型包
A full migration guide is available in MIGRATING.md.
完整的迁移指南可在 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. 非常欢迎反馈、Issue 和 PR。这是一个活跃的项目,我很乐意根据大家的实际需求来优先开发特定的验证器或插件。