ajv-formats
ajv-formats is the official plugin that gives Ajv v8 its format keyword validators. Ajv 8 removed built-in formats from the core, so a bare new Ajv() throws on format: 'email' in strict mode. One call, addFormats(ajv), registers the JSON Schema draft formats (date, time, date-time, duration, uri, uri-reference, email, hostname, ipv4, ipv6, uuid, regex, json-pointer) plus the OpenAPI data types (int32, int64, float, double, byte, binary, password). It also adds formatMinimum and formatMaximum keywords for range checks on dates and times.
If you run Ajv 8 with format keywords, this is not optional; install it and move on. Just know it is in low-maintenance mode and treat format checks as annotations with teeth, not as input security.
Use it if
- You use Ajv 8 with any schema containing format; without this plugin strict-mode Ajv throws 'unknown format' at compile time
- You validate OpenAPI 3.0 schemas, which lean on int32, int64, float, double, and byte formats that plain JSON Schema lacks
- You need date or time range validation declaratively: formatMinimum and formatExclusiveMaximum compare values within a format's ordering
- You want control over bundle size and behavior: you can register only the formats you use, and switch to fast mode for cheaper regexes
- You expect formats to be a security boundary: these are single regexes, the email and hostname patterns accept strings real mail servers reject, and the regex format feeds user input to the RegExp constructor, so treat formats as sanity checks and validate critical fields separately
- Maintenance pace matters to you: the last npm publish was March 2024, the last repo push was August 2024, and 53 issues sit open; it keeps working because it is essentially finished, but bug reports and PRs go unanswered
- You need internationalized formats (iri, iri-reference, idn-email, idn-hostname): they are not included here, you need the separate ajv-formats-draft2019 plugin
- You are choosing a validation stack fresh for a TypeScript app with no JSON Schema requirement: zod or similar gives you inferred types and refinements without the Ajv plugin dance
Setup reality
npm install ajv ajv-formats; the plugin declares ajv ^8.0.0 as both a dependency and an optional peer, so the real risk is two ajv copies in the tree, which produces baffling 'unknown keyword' or type mismatch errors until you dedupe. TypeScript under NodeNext module resolution hits the classic Ajv dual-package problem where the default import needs esModuleInterop or a .default call. And if you use the 2020-12 dialect you must instantiate Ajv2020 from ajv/dist/2020 and pass that instance to addFormats; the plain Ajv class only speaks draft-07.
Patterns
Register all formats on an Ajv instanceadd-all-formats
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv();
addFormats(ajv);
const validate = ajv.compile({ type: "string", format: "email" });
validate("a@b.co"); // trueCall addFormats before compiling any schema that uses format. In CommonJS use const addFormats = require("ajv-formats").
Validate email, uuid, and uri fields in one schemavalidate-common-formats
const schema = {
type: "object",
properties: {
id: { type: "string", format: "uuid" },
email: { type: "string", format: "email" },
website: { type: "string", format: "uri" },
},
required: ["id", "email"],
};
const validate = ajv.compile(schema);
if (!validate(data)) console.log(validate.errors);format only applies to strings; a numeric value passes a format check untouched because JSON Schema defines format per type.
Register only the formats you useregister-only-needed-formats
addFormats(ajv, ["date-time", "uuid"]);Any other format in your schemas will now throw at compile time under strict mode, which is a feature: it catches typos like 'datetime'.
Trade correctness for speed with fast modefast-mode
addFormats(ajv, { mode: "fast" });Fast mode simplifies date, time, date-time, uri, uri-reference, and email: dates stop range-checking, so 2026-02-31 passes structure-only validation. Only use it where inputs are already trusted.
Range-check dates with formatMinimum/formatMaximumdate-range-keywords
const schema = {
type: "string",
format: "date",
formatMinimum: "2026-01-01",
formatExclusiveMaximum: "2027-01-01",
};
const validate = ajv.compile(schema);
validate("2026-08-05"); // true
validate("2027-01-01"); // falseThese keywords are only registered when addFormats runs with no options or with keywords: true; if you pass any options object, remember to include keywords: true or compilation throws.
Use formats with the 2020-12 dialectdraft-2020-dialect
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020();
addFormats(ajv);The default Ajv class is draft-07. Schemas declaring $schema 2020-12 need the Ajv2020 class, and addFormats works on it the same way.
Allow formats you intentionally do not validateallow-unknown-formats
const ajv = new Ajv({ formats: { "customer-code": true } });
addFormats(ajv);Mapping a format name to true makes Ajv accept it as a no-op instead of throwing in strict mode; useful for vendor schemas with proprietary formats.
date-time requires a timezone; iso-date-time does notdate-time-timezone-gotcha
addFormats(ajv, ["date-time", "iso-date-time"]);
const strict = ajv.compile({ type: "string", format: "date-time" });
strict("2026-08-05T12:00:00"); // false, no timezone
strict("2026-08-05T12:00:00Z"); // true
const loose = ajv.compile({ type: "string", format: "iso-date-time" });
loose("2026-08-05T12:00:00"); // trueRFC 3339 date-time mandates an offset or Z. Local timestamps from HTML datetime-local inputs fail it; validate those with iso-date-time instead.
Validate OpenAPI integer formatsopenapi-number-formats
const schema = {
type: "object",
properties: {
count: { type: "integer", format: "int32" },
total: { type: "integer", format: "int64" },
},
};int64 checks the value is an integer in range, but JavaScript numbers lose precision past 2^53, so a true 64-bit ID has already been corrupted by JSON.parse before validation sees it; keep big IDs as strings.
Add your own format next to the standard onescustom-format-alongside
addFormats(ajv);
ajv.addFormat("semver", /^\d+\.\d+\.\d+(-[\w.]+)?$/);
const validate = ajv.compile({ type: "string", format: "semver" });
validate("1.2.3"); // trueajv.addFormat is core Ajv, not this plugin, and both register into the same namespace; defining a format twice overwrites silently, so avoid reusing standard names.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| ajv-formats-draft2019 | npm | You need the internationalized formats: iri, idn-email, idn-hostname |
| @cfworker/json-schema | npm | You need JSON Schema validation with formats included in environments that block code generation, like Cloudflare Workers |
| zod | npm | TypeScript-first validation with inferred types and no JSON Schema requirement |
| joi | npm | You prefer a chainable schema builder for Node config and request validation |