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 的核心价值所在。每一个插件都是一个专注且可组合的单元:

PluginWhat it does
插件功能描述
TriggerValidates on DOM events (blur, input, change)
Trigger在 DOM 事件(blur, input, change)时进行验证
MessageRenders error messages in the DOM
Message在 DOM 中渲染错误信息
IconShows ✓ / ✗ icons next to fields
Icon在字段旁边显示 ✓ / ✗ 图标
Bootstrap5Applies is-valid / is-invalid classes
Bootstrap5应用 is-valid / is-invalid 类名
TailwindApplies configurable Tailwind utility classes
Tailwind应用可配置的 Tailwind 工具类
AriaAdds aria-invalid and aria-describedby automatically
Aria自动添加 aria-invalid 和 aria-describedby 属性
SequenceStops at the first failing validator per field
Sequence在每个字段的第一个验证失败处停止
ExcludedSkips disabled or hidden fields
Excluded跳过被禁用或隐藏的字段
DeclarativeConfigures validators via data-vd-* HTML attributes
Declarative通过 data-vd-* HTML 属性配置验证器
PasswordStrengthScores password strength (0–4)
PasswordStrength评估密码强度 (0–4)
CharCounterLive character counter with overflow state
CharCounter带有溢出状态的实时字符计数器
SummaryRenders a consolidated error list in a container
Summary在容器中渲染汇总的错误列表
TransformerNormalises 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

尝试一下

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。这是一个活跃的项目,我很乐意根据大家的实际需求来优先开发特定的验证器或插件。