React LogoYup

Yup is a JavaScript schema builder for value parsing and validation. It allows developers to define the shape and requirements of data, then validate existing data against that schema. It's particularly popular in front-end development, especially when working with forms in libraries like React, Vue, or Angular, due to its declarative syntax and seamless integration with state management and form handling libraries (e.g., Formik, React Hook Form).

Core Concepts:
1. Schema Definition: You define a schema using `yup` methods like `object()`, `string()`, `number()`, `array()`, etc. These methods are chained to apply various validation rules.
2. Validation Rules: Each type (e.g., string) comes with numerous built-in validation methods (e.g., `required()`, `min()`, `max()`, `email()`, `matches()`).
3. Error Messages: You can provide custom error messages for each validation rule, making it user-friendly.
4. Asynchronous Validation: Yup supports asynchronous validation, which is useful for checking uniqueness against a database.
5. Type Coercion: It can automatically cast values to the correct type if necessary.
6. Composable Schemas: You can build complex schemas by combining simpler ones, promoting reusability.

Common Validation Types and Methods:
* `yup.string()`: `.required()`, `.min(length, message)`, `.max(length, message)`, `.email(message)`, `.url(message)`, `.matches(regex, message)`.
* `yup.number()`: `.required()`, `.min(value, message)`, `.max(value, message)`, `.positive(message)`, `.negative(message)`, `.integer(message)`.
* `yup.boolean()`: `.required()`.
* `yup.date()`: `.required()`, `.min(date, message)`, `.max(date, message)`.
* `yup.array()`: `.of(schema)`, `.min(length, message)`, `.max(length, message)`.
* `yup.object()`: `.shape({ fieldName: schema })`, used for nested objects.
* `yup.ref()`: Used to reference other fields in the schema (e.g., for password confirmation).

How it Works:
You create a schema, then pass your data to its `validate()` or `isValid()` method. `validate()` will throw a `ValidationError` if the data is invalid, providing detailed information about the errors, including their paths and messages. `isValid()` simply returns a boolean. You can configure `validate()` with `abortEarly: false` to gather all validation errors instead of stopping at the first one.

Yup significantly reduces boilerplate code for validation and improves the maintainability and reliability of forms and data handling in applications.

Example Code

```jsx
import React, { useState } from 'react';
import * as yup from 'yup';

// Define the validation schema using yup
// This schema describes the expected shape and validation rules for our form data.
const registrationSchema = yup.object().shape({
  username: yup.string()
    .required('Username is required')
    .min(3, 'Username must be at least 3 characters long'),
  email: yup.string()
    .email('Invalid email format')
    .required('Email is required'),
  password: yup.string()
    .required('Password is required')
    .min(6, 'Password must be at least 6 characters long'),
  confirmPassword: yup.string()
    .oneOf([yup.ref('password'), null], 'Passwords must match') // Checks if it matches the 'password' field
    .required('Confirm Password is required'),
  age: yup.number()
    .typeError('Age must be a number')
    .required('Age is required')
    .positive('Age must be a positive number')
    .integer('Age must be an integer')
    .min(18, 'You must be at least 18 years old'),
});

function RegistrationForm() {
  // State to hold form data
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
    age: '',
  });

  // State to hold validation errors
  const [errors, setErrors] = useState({});
  // State to indicate successful submission
  const [isSubmitted, setIsSubmitted] = useState(false);

  // Handle input changes, updating the form data state
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prevData => ({ ...prevData, [name]: value }));
    // Optionally clear error for the field as user types
    if (errors[name]) {
      setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
    }
    setIsSubmitted(false); // Reset submission status on change
  };

  // Handle form submission
  const handleSubmit = async (e) => {
    e.preventDefault(); // Prevent default browser form submission
    setErrors({}); // Clear previous errors
    setIsSubmitted(false);

    try {
      // Validate the form data against the schema
      // abortEarly: false ensures all validation errors are collected, not just the first one
      await registrationSchema.validate(formData, { abortEarly: false });
      console.log('Form is valid!', formData);
      setIsSubmitted(true);
      // In a real application, you would send formData to a server here
      // setFormData({ username: '', email: '', password: '', confirmPassword: '', age: '' }); // Clear form fields
    } catch (validationErrors) {
      console.log('Validation errors:', validationErrors);
      const newErrors = {};
      // Map validation errors to an object where keys are field names and values are error messages
      validationErrors.inner.forEach(error => {
        newErrors[error.path] = error.message;
      });
      setErrors(newErrors);
    }
  };

  return (
    <div style={formContainerStyle}>
      <h2>Register with Yup</h2>
      {isSubmitted && <p style={successMessageStyle}>Registration successful!</p>}
      <form onSubmit={handleSubmit}>
        <div style={fieldGroupStyle}>
          <label htmlFor="username" style={labelStyle}>Username:</label>
          <input
            type="text"
            id="username"
            name="username"
            value={formData.username}
            onChange={handleChange}
            style={inputStyle}
          />
          {errors.username && <p style={errorStyle}>{errors.username}</p>}
        </div>

        <div style={fieldGroupStyle}>
          <label htmlFor="email" style={labelStyle}>Email:</label>
          <input
            type="email"
            id="email"
            name="email"
            value={formData.email}
            onChange={handleChange}
            style={inputStyle}
          />
          {errors.email && <p style={errorStyle}>{errors.email}</p>}
        </div>

        <div style={fieldGroupStyle}>
          <label htmlFor="password" style={labelStyle}>Password:</label>
          <input
            type="password"
            id="password"
            name="password"
            value={formData.password}
            onChange={handleChange}
            style={inputStyle}
          />
          {errors.password && <p style={errorStyle}>{errors.password}</p>}
        </div>

        <div style={fieldGroupStyle}>
          <label htmlFor="confirmPassword" style={labelStyle}>Confirm Password:</label>
          <input
            type="password"
            id="confirmPassword"
            name="confirmPassword"
            value={formData.confirmPassword}
            onChange={handleChange}
            style={inputStyle}
          />
          {errors.confirmPassword && <p style={errorStyle}>{errors.confirmPassword}</p>}
        </div>

        <div style={fieldGroupStyle}>
          <label htmlFor="age" style={labelStyle}>Age:</label>
          <input
            type="number"
            id="age"
            name="age"
            value={formData.age}
            onChange={handleChange}
            style={inputStyle}
          />
          {errors.age && <p style={errorStyle}>{errors.age}</p>}
        </div>

        <button type="submit" style={buttonStyle}>
          Register
        </button>
      </form>
    </div>
  );
}

// Basic inline styles for demonstration
const formContainerStyle = {
  maxWidth: '450px',
  margin: '50px auto',
  padding: '30px',
  border: '1px solid #ddd',
  borderRadius: '10px',
  boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
  backgroundColor: '#fff',
};

const fieldGroupStyle = {
  marginBottom: '20px',
};

const labelStyle = {
  display: 'block',
  marginBottom: '8px',
  fontWeight: 'bold',
  color: '#333',
};

const inputStyle = {
  width: '100%',
  padding: '12px',
  border: '1px solid #ccc',
  borderRadius: '6px',
  fontSize: '1em',
  boxSizing: 'border-box', // Include padding in width
};

const errorStyle = {
  color: '#e74c3c',
  fontSize: '0.85em',
  marginTop: '5px',
  fontWeight: 'normal',
};

const buttonStyle = {
  width: '100%',
  padding: '12px 20px',
  backgroundColor: '#007bff',
  color: 'white',
  border: 'none',
  borderRadius: '6px',
  fontSize: '1.1em',
  cursor: 'pointer',
  marginTop: '10px',
  transition: 'background-color 0.3s ease',
};

const successMessageStyle = {
  color: '#28a745',
  fontSize: '1.1em',
  textAlign: 'center',
  marginBottom: '20px',
  fontWeight: 'bold',
};

export default RegistrationForm;
```