# Custom Validation

Canonical page: https://docs.ovok.com/native-sdk/guide/components/auth/register/examples/custom-validation

Learn how to implement custom validation rules for the registration form.

## Custom Validation Rules [#custom-validation-rules]

Override default validation with custom Yup schemas:

```tsx
import React from "react";
import * as Yup from "yup";
import { Register } from "@ovok/native";

export const CustomValidationRegistration = () => {
  const customValidations = {
    name: Yup.string()
      .required("First name is required")
      .min(3, "Name must be at least 3 characters")
      .max(20, "Name cannot exceed 20 characters")
      .matches(/^[a-zA-Z\s]+$/, "Name can only contain letters"),

    surname: Yup.string()
      .required("Last name is required")
      .min(2, "Last name must be at least 2 characters")
      .max(30, "Last name cannot exceed 30 characters"),

    email: Yup.string()
      .email("Please enter a valid email address")
      .required("Email is required")
      .matches(
        /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
        "Please enter a valid email format",
      ),

    password: (existingRule) =>
      existingRule
        .min(10, "Password must be at least 10 characters")
        .matches(/[A-Z]/, "Password must contain at least one uppercase letter")
        .matches(/[a-z]/, "Password must contain at least one lowercase letter")
        .matches(/[0-9]/, "Password must contain at least one number")
        .matches(
          /[^A-Za-z0-9]/,
          "Password must contain at least one special character",
        ),

    passwordConfirm: (existingRule) =>
      existingRule.required("Please confirm your password"),
  };

  return (
    <Register>
      <Register.Header>
        <Register.Header.Title>Create Strong Account</Register.Header.Title>
        <Register.Header.Description>
          Please follow our security requirements
        </Register.Header.Description>
      </Register.Header>

      <Register.EmailForm
        customValidations={customValidations}
        tenantCode="your-tenant-code"
        onSuccess={(response) => console.log("Success:", response)}
        onError={(error) => console.error("Error:", error)}
      >
        <Register.EmailForm.Inputs />
        <Register.EmailForm.RegisterButton />
      </Register.EmailForm>
    </Register>
  );
};
```

## Domain-specific Validation [#domain-specific-validation]

Validation rules for specific business requirements:

```tsx
import React from "react";
import * as Yup from "yup";
import { Register } from "@ovok/native";

export const BusinessValidationRegistration = () => {
  // Custom validation for business domains
  const allowedDomains = ["company.com", "partner.org", "client.net"];

  const customValidations = {
    email: Yup.string()
      .email("Invalid email format")
      .required("Corporate email is required")
      .test("domain", "Please use a corporate email address", (value) => {
        if (!value) return false;
        const domain = value.split("@")[1];
        return allowedDomains.includes(domain);
      }),

    name: Yup.string()
      .required("Full name is required")
      .min(2, "Name too short")
      .test("full-name", "Please enter your full name", (value) => {
        return value && value.trim().split(" ").length >= 2;
      }),

    password: (existingRule) =>
      existingRule
        .min(12, "Corporate password must be at least 12 characters")
        .matches(/[A-Z]{2,}/, "Must contain at least 2 uppercase letters")
        .matches(/[0-9]{2,}/, "Must contain at least 2 numbers")
        .matches(/[!@#$%^&*]/, "Must contain special characters (!@#$%^&*)")
        .test("no-common", "Password is too common", (value) => {
          const commonPasswords = ["password123", "123456789", "qwerty123"];
          return !commonPasswords.includes(value?.toLowerCase());
        }),
  };

  return (
    <Register>
      <Register.Header>
        <Register.Header.Title>Corporate Registration</Register.Header.Title>
        <Register.Header.Description>
          Please use your corporate email address
        </Register.Header.Description>
      </Register.Header>

      <Register.EmailForm
        customValidations={customValidations}
        tenantCode="your-tenant-code"
        onSuccess={(response) =>
          console.log("Corporate registration:", response)
        }
        onError={(error) => console.error("Registration error:", error)}
      >
        <Register.EmailForm.Inputs />
        <Register.EmailForm.RegisterButton />
      </Register.EmailForm>
    </Register>
  );
};
```

## Async Validation [#async-validation]

Validate against server data (email uniqueness, etc.):

```tsx
import React from "react";
import * as Yup from "yup";
import { Register } from "@ovok/native";

export const AsyncValidationRegistration = () => {
  // Mock API function to check email availability
  const checkEmailAvailability = async (email: string) => {
    // Simulate API call
    const response = await fetch(`/api/check-email?email=${email}`);
    const data = await response.json();
    return data.available;
  };

  const customValidations = {
    email: Yup.string()
      .email("Invalid email format")
      .required("Email is required")
      .test(
        "email-available",
        "This email is already registered",
        async (value) => {
          if (!value || !Yup.string().email().isValidSync(value)) {
            return true; // Let email format validation handle this
          }

          try {
            const isAvailable = await checkEmailAvailability(value);
            return isAvailable;
          } catch (error) {
            // If API fails, allow the form to proceed
            console.warn("Email availability check failed:", error);
            return true;
          }
        },
      ),

    name: Yup.string()
      .required("Username is required")
      .min(3, "Username must be at least 3 characters")
      .matches(
        /^[a-zA-Z0-9_]+$/,
        "Username can only contain letters, numbers, and underscores",
      )
      .test("username-available", "This username is taken", async (value) => {
        if (!value || value.length < 3) return true;

        try {
          const response = await fetch(`/api/check-username?username=${value}`);
          const data = await response.json();
          return data.available;
        } catch (error) {
          return true;
        }
      }),
  };

  return (
    <Register>
      <Register.Header>
        <Register.Header.Title />
        <Register.Header.Description />
      </Register.Header>

      <Register.EmailForm
        customValidations={customValidations}
        tenantCode="your-tenant-code"
        onSuccess={(response) => console.log("Success:", response)}
        onError={(error) => console.error("Error:", error)}
      >
        <Register.EmailForm.Inputs />
        <Register.EmailForm.RegisterButton />
      </Register.EmailForm>
    </Register>
  );
};
```

## Password Strength Validation [#password-strength-validation]

Advanced password requirements with visual feedback:

```tsx
import React from "react";
import * as Yup from "yup";
import { Register } from "@ovok/native";

export const StrongPasswordRegistration = () => {
  const customValidations = {
    password: (existingRule) =>
      existingRule
        .min(8, "Password must be at least 8 characters")
        .test(
          "password-strength",
          "Password does not meet requirements",
          (value) => {
            if (!value) return false;

            const requirements = [
              { test: /.{8,}/, message: "At least 8 characters" },
              { test: /[A-Z]/, message: "One uppercase letter" },
              { test: /[a-z]/, message: "One lowercase letter" },
              { test: /[0-9]/, message: "One number" },
              { test: /[^A-Za-z0-9]/, message: "One special character" },
            ];

            return requirements.every((req) => req.test.test(value));
          },
        ),

    passwordConfirm: (existingRule) =>
      existingRule
        .required("Please confirm your password")
        .test("passwords-match", "Passwords do not match", function (value) {
          return value === this.parent.password;
        }),
  };

  return (
    <Register>
      <Register.Header>
        <Register.Header.Title>Secure Registration</Register.Header.Title>
        <Register.Header.Description>
          Create a strong password to protect your account
        </Register.Header.Description>
      </Register.Header>

      <Register.EmailForm
        customValidations={customValidations}
        tenantCode="your-tenant-code"
        onSuccess={(response) => console.log("Success:", response)}
        onError={(error) => console.error("Error:", error)}
      >
        <Register.EmailForm.Inputs />
        <Register.EmailForm.RegisterButton />
      </Register.EmailForm>
    </Register>
  );
};
```

## International Validation [#international-validation]

Support for international names and addresses:

```tsx
import React from "react";
import * as Yup from "yup";
import { Register } from "@ovok/native";

export const InternationalRegistration = () => {
  const customValidations = {
    name: Yup.string()
      .required("Name is required")
      .min(1, "Name is required")
      .max(50, "Name is too long")
      // Support international characters
      .matches(/^[\p{L}\p{M}\s\-'\.]+$/u, "Name contains invalid characters"),

    surname: Yup.string()
      .required("Surname is required")
      .min(1, "Surname is required")
      .max(50, "Surname is too long")
      .matches(
        /^[\p{L}\p{M}\s\-'\.]+$/u,
        "Surname contains invalid characters",
      ),

    email: Yup.string()
      .email("Invalid email format")
      .required("Email is required")
      // Support international domain names
      .matches(
        /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
        "Please enter a valid email address",
      ),
  };

  return (
    <Register>
      <Register.Header>
        <Register.Header.Title>
          International Registration
        </Register.Header.Title>
        <Register.Header.Description>
          Welcome users from around the world
        </Register.Header.Description>
      </Register.Header>

      <Register.EmailForm
        customValidations={customValidations}
        tenantCode="your-tenant-code"
        onSuccess={(response) => console.log("Success:", response)}
        onError={(error) => console.error("Error:", error)}
      >
        <Register.EmailForm.Inputs />
        <Register.EmailForm.RegisterButton />
      </Register.EmailForm>
    </Register>
  );
};
```

## Validation Features [#validation-features]

- **Real-time Validation**: Validation occurs as the user types
- **Custom Error Messages**: Provide specific, helpful error messages
- **Async Validation**: Check against server data for uniqueness
- **Password Strength**: Complex password requirements
- **International Support**: Unicode character support for global users
- **Business Rules**: Domain-specific validation requirements

## Best Practices [#best-practices]

1. **Clear Error Messages**: Use descriptive, actionable error messages
2. **Progressive Validation**: Start with basic rules, add complexity as needed
3. **User-Friendly**: Balance security with usability
4. **Performance**: Be mindful of async validation performance
5. **Accessibility**: Ensure error messages are accessible to screen readers

## Next Steps [#next-steps]

- [Custom Styling](/native-sdk/guide/components/auth/register/examples/custom-styling) - Style validation states and error messages
- [Pre-filled Data](/native-sdk/guide/components/auth/register/examples/pre-filled-data) - Handle validation with default values
