Rilaykit Logorilaykit ✨
Migration

Migrating to Standard Schema

Learn how to migrate from validation-adapters to the new Standard Schema system.

RilayKit has migrated from custom validation adapters to Standard Schema, providing universal validation library support without adapters.

What Changed

Old System (Removed)

  • @rilaykit/validation-adapters package with separate adapters for each library
  • Confusing API with both validators and schema properties
  • Custom adapter code for each validation library

New System (Standard Schema)

  • Universal validate property that works with any Standard Schema library
  • Direct usage of Zod, Yup, Joi, and other compatible libraries
  • No adapters needed - libraries work natively
  • Unified, intuitive API

Migration Guide

Package Changes

# Remove the old validation-adapters package
npm uninstall @rilaykit/validation-adapters
# or
pnpm remove @rilaykit/validation-adapters
# or
yarn remove @rilaykit/validation-adapters
# Add validation libraries directly (no adapters needed!)
npm install zod yup joi
# or
pnpm add zod yup joi
# or
yarn add zod yup joi

# Pick any Standard Schema compatible library
npm install valibot arktype # etc.

API Changes

The main change is using the validate property instead of validators + schema:

// ❌ Old confusing API
import { createZodValidator } from '@rilaykit/validation-adapters';
import { required } from '@rilaykit/core';
import { z } from 'zod';

const emailValidator = createZodValidator(z.string().email());

const form = rilay.form('user')
  .add({
    id: 'email',
    type: 'input',
    validation: {
      validators: [required()],     // One way
      schema: z.string().email(),   // Another way - confusing!
    },
  });
// ✅ New unified API
import { required } from '@rilaykit/core';
import { z } from 'zod';

const form = rilay.form('user')
  .add({
    id: 'email',
    type: 'input',
    validation: {
      validate: [required(), z.string().email()], // One unified way!
    },
  });

Step-by-Step Migration

1. Update Imports

// Remove these imports
import { 
  createZodValidator,
  createZodFormValidator,
} from '@rilaykit/validation-adapters/zod';

import { 
  createYupValidator,
  createYupFormValidator,
} from '@rilaykit/validation-adapters/yup';
// Use validation libraries directly
import { z } from 'zod';
import * as yup from 'yup';
import Joi from 'joi';

// RilayKit built-ins still available
import { required, email, minLength } from '@rilaykit/core';

2. Update Field Validation

// ❌ Old adapter-based approach
const emailValidator = createZodValidator(z.string().email());

.add({
  id: 'email',
  type: 'input',
  validation: {
    validators: [required(), emailValidator],
  },
})
// ✅ Direct Standard Schema usage
.add({
  id: 'email',
  type: 'input',
  validation: {
    validate: [required(), z.string().email()],
  },
})

3. Update Form Validation

// ❌ Old form validator approach
const formValidator = createZodFormValidator(z.object({
  email: z.string().email(),
  password: z.string().min(8),
}));

.setValidation({
  validators: [formValidator],
})
// ✅ Direct object schema usage
const userSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

.setValidation({
  validate: userSchema,
})

Advanced Migration Examples

Complex Form with Cross-Field Validation

import { createZodFormValidator } from '@rilaykit/validation-adapters';

const userSchema = z.object({
  password: z.string().min(8),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
});

const formValidator = createZodFormValidator(userSchema);

const form = rilay.form('user')
  .add({ id: 'password', type: 'password' })
  .add({ id: 'confirmPassword', type: 'password' })
  .setValidation({
    validators: [formValidator],
  });
import { z } from 'zod';

const userSchema = z.object({
  password: z.string().min(8),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
});

const form = rilay.form('user')
  .add({ id: 'password', type: 'password' })
  .add({ id: 'confirmPassword', type: 'password' })
  .setValidation({
    validate: userSchema, // Direct usage!
  });

Mixed Library Usage

import { z } from 'zod';
import * as yup from 'yup';
import Joi from 'joi';
import { required, email } from '@rilaykit/core';

const advancedForm = rilay
  .form('advanced')
  .add({
    id: 'email',
    type: 'input',
    validation: {
      validate: [
        required('Email is required'),           // RilayKit
        z.string().email('Invalid format'),      // Zod
        yup.string().max(100, 'Too long'),       // Yup
        Joi.string().min(5, 'Too short'),        // Joi
      ]
    }
  })
  .add({
    id: 'phone',
    type: 'input',
    validation: {
      validate: z.string().regex(/^\+?[\d\s-()]+$/, 'Invalid phone format'),
    }
  });

Benefits of Standard Schema

Why we migrated: Standard Schema provides better performance, universal compatibility, and a cleaner API. It's supported by major validation libraries and ecosystem tools like tRPC, TanStack Form, and Hono.

Performance Improvements

  • No adapter overhead: Direct validation execution
  • Smaller bundle size: No adapter packages needed
  • Better tree-shaking: Only include what you use

Developer Experience

  • One API to learn: Single validate property
  • No adapter maintenance: Libraries work directly
  • Universal compatibility: Same API across all libraries
  • Future-proof: New libraries work automatically if they support Standard Schema

Ecosystem Alignment

  • tRPC: Accepts Standard Schema for API validation
  • TanStack Form: Uses Standard Schema for form validation
  • Hono: Middleware supports Standard Schema
  • And many more: See standardschema.dev for the full list

Need Help?

If you're having trouble migrating, check out our examples or reach out on our GitHub discussions.

On this page