Free & open source — no account required

v1.2.5-PRICING-19
Web & Frontend • Engineering Documentation

Yup Mastery: Mastering Form Validation Generation

This technical guide provides an in-depth analysis of the json to yup engine, best practices for implementation, and data security standards.

JSON to Yup: Generating Form Validation Schemas from Data Structures

Yup is the validation library of choice in Formik-based React forms, but writing schemas by hand for complex nested objects is tedious. Generating a Yup schema from your JSON gives you a correct schema shape instantly — with .required() and .nullable() inferred from your data, enum values converted to .oneOf(), and email/URL formats detected automatically. Use the generated schema as a starting point, then layer in business rules with .when() and .test().

Live Example: User Registration Form

// Input JSON (sample form payload)
{
  "username": "dev_guru",
  "email": "dev@example.com",
  "age": 28,
  "role": "admin",
  "website": "https://example.com",
  "bio": null,
  "address": {
    "city": "Tokyo",
    "country": "JP"
  }
}

// Generated Yup Schema
import * as yup from 'yup';

export const rootAddressYupSchema = yup.object({
  city: yup.string().required(),
  country: yup.string().required(),
});

export const rootYupSchema = yup.object({
  username: yup.string().required(),
  email: yup.string().email().required(),
  age: yup.number().required(),
  role: yup.string().oneOf(["admin"]).required(),
  website: yup.string().url().required(),
  bio: yup.string().nullable(),
  address: rootAddressYupSchema.required(),
});

Adding Business Rules with .when() and .test()

The generator covers the structural schema. Business rules go on top:

import * as yup from 'yup';

const registrationSchema = yup.object({
  username: yup.string()
    .min(3, 'Username must be at least 3 characters')
    .max(20, 'Username cannot exceed 20 characters')
    .matches(/^[a-z0-9_]+$/, 'Only lowercase letters, numbers and underscores')
    .required(),

  email: yup.string().email().required(),

  password: yup.string()
    .min(8, 'Password must be at least 8 characters')
    .required(),

  confirmPassword: yup.string()
    .oneOf([yup.ref('password')], 'Passwords must match')
    .required(),

  age: yup.number()
    .integer('Age must be a whole number')
    .min(13, 'Must be at least 13 years old')
    .max(120)
    .required(),

  // Conditional: website required only for 'creator' role
  role: yup.string().oneOf(['user', 'creator', 'admin']).required(),
  website: yup.string().url().when('role', {
    is: 'creator',
    then: (schema) => schema.required('Website required for creators'),
    otherwise: (schema) => schema.optional(),
  }),
});

Custom Validators with .test()

const schemaWithCustom = yup.object({
  username: yup.string()
    .required()
    .test(
      'unique-username',
      'Username is already taken',
      async (value) => {
        if (!value) return true;
        const exists = await api.checkUsernameAvailability(value);
        return !exists;
      }
    ),

  phone: yup.string()
    .test(
      'valid-phone',
      'Enter a valid phone number',
      (value) => {
        if (!value) return true; // optional — skip if empty
        return /^\+?[\d\s\-().]{7,15}$/.test(value);
      }
    ),
});

Async tests like the username check run only when abortEarly: false is set and all sync validations pass first. Use sparingly — every async test is an API call.

Integrating with Formik

import { Formik, Form, Field, ErrorMessage } from 'formik';

function RegistrationForm() {
  return (
    <Formik
      initialValues={{ username: '', email: '', age: '', role: 'user', website: '' }}
      validationSchema={registrationSchema}  // ← generated schema goes here
      onSubmit={async (values, { setSubmitting }) => {
        await api.register(values);
        setSubmitting(false);
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field name="username" />
          <ErrorMessage name="username" component="div" className="text-red-500" />

          <Field name="email" type="email" />
          <ErrorMessage name="email" component="div" className="text-red-500" />

          <button type="submit" disabled={isSubmitting}>Register</button>
        </Form>
      )}
    </Formik>
  );
}

Yup vs Zod: When to Use Which

Both validate data, but they have different philosophies:

  • Yup: Class-based, schemas are mutable objects. Schemas compose via .concat(), conditional logic via .when(). Standard choice for Formik. Async validation built in.
  • Zod: Functional, schemas are immutable. Type inference is first-class — z.infer<typeof schema> gives you the TypeScript type for free. Standard choice for tRPC, server-side validation, and new projects.

If you're starting fresh, Zod's TypeScript integration is tighter. If you're using Formik or have existing Yup schemas, stay with Yup — mixing them adds complexity with no benefit.

Frequently Asked Questions

Does it handle nested objects? Yes. Nested JSON objects generate separate yup.object() schemas, referenced by the parent schema by name.

How does it handle arrays of objects? Arrays of objects generate a child schema and use yup.array().of(childSchema). Arrays of primitives use yup.array().of(yup.string()) etc.

What does it do with enum-like string fields? If a string field has a detected set of values, it generates .oneOf(["value1", "value2"]). If it's a free-form string, just yup.string().

Is my JSON sent to a server? No. TypeMorph runs entirely in your browser — form payloads often contain personal data, and none of it leaves your machine.

Developer FAQ

Is the processing local-only?

Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.

Can I use this for enterprise projects?

Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.