npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nexus-state/form-schema-ajv

v0.1.3

Published

AJV (JSON Schema) validator plugin for Nexus State forms

Readme

@nexus-state/form-schema-ajv

AJV (JSON Schema) validator plugin for Nexus State forms

npm version TypeScript License


🎯 When to Use AJV

✅ Choose AJV if you need:

| Use Case | Why AJV | |----------|---------| | JSON Schema standard | Industry standard, cross-language | | Declarative validation | Describe what, not how | | Performance | Fastest JSON Schema validator | | Custom keywords | Extend with your own rules |

❌ Use alternatives if:

| Use Case | Better Alternative | |----------|-------------------| | TypeScript-first | @nexus-state/form-schema-zod | | Simple API | @nexus-state/form-schema-yup | | Simple DSL | @nexus-state/form-schema-dsl |


🎯 Overview

AJV plugin integrates AJV (JSON Schema) validation with Nexus State forms. AJV is the fastest and most popular JSON Schema validator for JavaScript.

Why JSON Schema / AJV?

  • ✅ Industry standard — widely adopted across languages
  • ✅ Declarative — describe what, not how
  • ✅ Rich validation — 20+ built-in keywords
  • ✅ Custom keywords — extend with your own rules
  • ✅ Fast — optimized for performance

📦 Installation

npm install @nexus-state/form-schema-ajv ajv

Peer dependencies:

  • @nexus-state/form — core form package
  • ajv — JSON Schema validator

🚀 Quick Start

Basic Form with JSON Schema

import { createForm } from '@nexus-state/form';
import { createStore } from '@nexus-state/core';
import { ajvPlugin } from '@nexus-state/form-schema-ajv';

const store = createStore();

// Define JSON Schema
const loginSchema = {
  type: 'object',
  properties: {
    email: {
      type: 'string',
      format: 'email',
    },
    password: {
      type: 'string',
      minLength: 8,
    },
  },
  required: ['email', 'password'],
};

// Create form with AJV plugin
const loginForm = createForm(store, {
  schemaPlugin: ajvPlugin,
  schemaConfig: {
    schema: loginSchema,
  },
  initialValues: {
    email: '',
    password: '',
  },
});

// Validate form
const isValid = await loginForm.validate();
if (isValid) {
  console.log('Form is valid!');
} else {
  console.log('Errors:', loginForm.errors);
}

📖 API Reference

Schema Configuration

interface AjvFormOptions {
  schemaType: 'ajv';
  schemaConfig: AjvSchemaConfig;
  initialValues: Record<string, unknown>;
  defaultValidationMode?: 'onChange' | 'onBlur' | 'onSubmit';
  showErrorsOnTouched?: boolean;
}

interface AjvSchemaConfig {
  schema: object; // JSON Schema object
  ajvOptions?: Options; // AJV options
  keywords?: KeywordDefinition[]; // Custom keywords
  formats?: Record<string, string | RegExp | ((value: string) => boolean)>; // Custom formats
}

AJV Options

import { Options } from 'ajv';

const config: AjvSchemaConfig = {
  schema: mySchema,
  ajvOptions: {
    allErrors: true,      // Collect all errors
    useDefaults: true,    // Use default values from schema
    strict: false,        // Less strict mode
  },
};

📝 Examples

1. Registration Form

const registrationSchema = {
  type: 'object',
  properties: {
    username: {
      type: 'string',
      minLength: 3,
      maxLength: 20,
      pattern: '^[a-zA-Z0-9_]+$',
    },
    email: {
      type: 'string',
      format: 'email',
    },
    password: {
      type: 'string',
      minLength: 8,
    },
    age: {
      type: 'number',
      minimum: 18,
      maximum: 120,
    },
    terms: {
      type: 'boolean',
      enum: [true],
    },
  },
  required: ['username', 'email', 'password', 'age', 'terms'],
};

2. Custom Error Messages

const schema = {
  type: 'object',
  properties: {
    email: {
      type: 'string',
      format: 'email',
      errorMessage: {
        type: 'Email must be a string',
        format: 'Please enter a valid email address',
      },
    },
    password: {
      type: 'string',
      minLength: 8,
      errorMessage: {
        minLength: 'Password must be at least 8 characters',
      },
    },
  },
  required: ['email', 'password'],
  errorMessage: {
    required: {
      email: 'Email is required',
      password: 'Password is required',
    },
  },
};

// Enable ajv-errors plugin
const config: AjvSchemaConfig = {
  schema,
  keywords: [require('ajv-errors')],
};

3. Custom Formats

import { builtInFormats } from '@nexus-state/form-schema-ajv';

const config: AjvSchemaConfig = {
  schema: {
    type: 'object',
    properties: {
      email: { type: 'string', format: 'email' },
      url: { type: 'string', format: 'uri' },
      uuid: { type: 'string', format: 'uuid' },
      date: { type: 'string', format: 'date' },
      ipv4: { type: 'string', format: 'ipv4' },
    },
  },
  formats: {
    // Override or add custom formats
    phone: /^\+?[1-9]\d{1,14}$/,
    postalCode: /^\d{5}(-\d{4})?$/,
  },
};

4. Custom Keywords

import { createCustomKeyword } from '@nexus-state/form-schema-ajv';

// Create custom "adult" keyword
const adultKeyword = createCustomKeyword({
  keyword: 'adult',
  type: 'number',
  validate: (schema: number, data: number) => data >= schema,
  errors: true,
});

const config: AjvSchemaConfig = {
  schema: {
    type: 'object',
    properties: {
      age: {
        type: 'number',
        adult: 18, // Custom keyword
      },
    },
  },
  keywords: [adultKeyword],
};

5. Default Values

const schema = {
  type: 'object',
  properties: {
    newsletter: {
      type: 'boolean',
      default: false,
    },
    role: {
      type: 'string',
      default: 'user',
    },
    preferences: {
      type: 'object',
      default: {
        theme: 'light',
        language: 'en',
      },
    },
  },
};

// With useDefaults: true, defaults are applied during validation
const config: AjvSchemaConfig = {
  schema,
  ajvOptions: {
    useDefaults: true,
  },
};

6. Conditional Validation

const schema = {
  type: 'object',
  properties: {
    needsShipping: { type: 'boolean' },
    shippingAddress: {
      type: 'object',
      properties: {
        street: { type: 'string' },
        city: { type: 'string' },
        zip: { type: 'string' },
      },
      required: ['street', 'city', 'zip'],
    },
  },
  if: {
    properties: {
      needsShipping: { const: true },
    },
  },
  then: {
    required: ['shippingAddress'],
  },
};

7. Nested Objects

const addressSchema = {
  type: 'object',
  properties: {
    street: { type: 'string', minLength: 1 },
    city: { type: 'string', minLength: 1 },
    state: { type: 'string', minLength: 2, maxLength: 2 },
    zip: { type: 'string', pattern: '^\\d{5}(-\\d{4})?$' },
  },
  required: ['street', 'city', 'state', 'zip'],
};

const userSchema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    email: { type: 'string', format: 'email' },
    address: addressSchema,
  },
  required: ['name', 'email', 'address'],
};

8. Arrays

const skillsSchema = {
  type: 'object',
  properties: {
    skills: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          level: { type: 'number', minimum: 1, maximum: 5 },
        },
        required: ['name', 'level'],
      },
      minItems: 1,
    },
  },
  required: ['skills'],
};

🔧 Advanced Usage

Built-in Formats

import { builtInFormats } from '@nexus-state/form-schema-ajv';

// Available formats:
// - email
// - uri
// - uuid
// - date
// - date-time
// - time
// - ipv4
// - ipv6

Async Validation

JSON Schema doesn't natively support async validation. For async validation (e.g., uniqueness checks), combine with custom validators:

// Use form-level async validation
const form = createForm(store, {
  schemaType: 'ajv',
  schemaConfig: { schema: mySchema },
  asyncValidators: {
    email: async (value) => {
      const response = await fetch(`/api/check-email?email=${value}`);
      const data = await response.json();
      return data.available ? null : 'Email is already registered';
    },
  },
});

Schema Reuse with $ref

const schema = {
  $defs: {
    address: {
      type: 'object',
      properties: {
        street: { type: 'string' },
        city: { type: 'string' },
        zip: { type: 'string' },
      },
      required: ['street', 'city', 'zip'],
    },
  },
  type: 'object',
  properties: {
    billingAddress: { $ref: '#/$defs/address' },
    shippingAddress: { $ref: '#/$defs/address' },
  },
};

⚠️ Troubleshooting

Issue: "validateField always returns null"

Explanation: AJV validates the entire schema, not individual fields. The plugin's validateField method returns null and relies on full validate() for proper validation.

Solution: Use form.validate() instead of field-level validation.

Issue: Custom formats not working

Solution: Register formats in the config:

const config: AjvSchemaConfig = {
  schema: mySchema,
  formats: {
    phone: /^\+?[1-9]\d{1,14}$/,
  },
};

Issue: Custom keywords not recognized

Solution: Register keywords in the config:

const config: AjvSchemaConfig = {
  schema: mySchema,
  keywords: [myCustomKeyword],
};

📚 Resources


🔗 See Also

Full ecosystem: Nexus State Packages


📄 License

MIT © Nexus State Contributors