How to Validate Phone Numbers with Yup: Require 8 Digits (Not Just Minimum Value)

Phone number validation is a critical part of user input handling in web applications. Incorrect or poorly formatted phone numbers can lead to failed communications, user frustration, and even data integrity issues. While many validation libraries exist, Yup has emerged as a popular choice for schema-based validation, especially in React ecosystems (often paired with form libraries like Formik).

A common requirement is ensuring phone numbers have exactly 8 digits—not more, not less. This is trickier than it sounds: using a simple "minimum 8 digits" check (e.g., min(8)) allows numbers with 9+ digits to slip through, which is unacceptable for systems requiring fixed-length inputs.

In this guide, we’ll walk through how to use Yup to enforce exactly 8 digits for phone numbers, covering setup, common pitfalls, edge cases, and integration with forms.

Table of Contents#

  1. Understanding the Requirement: Exactly 8 Digits vs. Minimum 8
  2. Setting Up Yup
  3. Basic Phone Number Validation (and Its Limitations)
  4. Requiring Exactly 8 Digits: The Core Solution
  5. Handling Edge Cases
  6. Integrating with Form Libraries (e.g., Formik)
  7. Testing the Validation
  8. Troubleshooting Common Issues
  9. Conclusion
  10. References

Understanding the Requirement: Exactly 8 Digits vs. Minimum 8#

Before diving into code, let’s clarify the requirement:

  • Minimum 8 digits: Allows 8, 9, 10, or more digits (e.g., 12345678, 123456789).
  • Exactly 8 digits: Only allows 8 digits (e.g., 12345678).

Many systems (e.g., internal tools, local services) require fixed-length phone numbers. For example, some countries or regions use 8-digit local numbers, and extra digits would render the number invalid. Using min(8) alone fails here, as it doesn’t cap the length.

Setting Up Yup#

First, ensure Yup is installed in your project. Yup works with any JavaScript/TypeScript project, but it’s most commonly used with React and form libraries like Formik or React Hook Form.

Installation#

Install Yup via npm or yarn:

# npm
npm install yup
 
# yarn
yarn add yup

Import Yup#

Import Yup into your component or validation file:

import * as Yup from 'yup';

Basic Phone Number Validation (and Its Limitations)#

Let’s start with a naive approach and see why it fails. A common mistake is using min(8) to enforce "at least 8 digits":

Example: Using min(8) (Insufficient)#

const phoneSchema = Yup.object().shape({
  phone: Yup.string()
    .min(8, 'Phone number must be at least 8 digits') // ❌ Allows 9+ digits
    .required('Phone number is required'),
});

Problem: This schema passes for 123456789 (9 digits) because it meets the "minimum 8" condition but violates the "exactly 8" requirement.

Requiring Exactly 8 Digits: The Core Solution#

To enforce exactly 8 digits, we need two checks:

  1. The input must be a string of exactly 8 characters.
  2. Those characters must be digits only (no letters, spaces, or special characters).

Step 1: Use length(8) for Exact Character Count#

Yup’s string() schema has a .length() method to enforce an exact character count:

Yup.string().length(8, 'Phone number must be exactly 8 characters');

But this alone isn’t enough: it allows non-digit characters (e.g., 1234abcd would pass if length(8) is used without digit checks).

Step 2: Enforce Digits Only with Regex#

To ensure the 8 characters are digits, use matches() with a regular expression (regex) that tests for exactly 8 digits:

Regex Pattern: ^\d{8}$

  • ^ = Start of string
  • \d = Any digit (0-9)
  • {8} = Exactly 8 occurrences
  • $ = End of string

Combined Schema: Exactly 8 Digits#

Merge these checks into a single schema:

const phoneSchema = Yup.object().shape({
  phone: Yup.string()
    .matches(/^\d{8}$/, 'Phone number must be exactly 8 digits (no spaces or special characters)') // ✅ Ensures 8 digits only
    .required('Phone number is required'), // ✅ Mandatory field
});

How It Works#

  • .matches(/^\d{8}$/, ...): Ensures the input is exactly 8 digits (no letters, spaces, hyphens, etc.).
  • .required(): Ensures the field isn’t empty.

Handling Edge Cases#

1. Leading Zeros#

The regex \d includes 0-9, so leading zeros (e.g., 01234567) are allowed by default. If your use case prohibits leading zeros (e.g., "must start with 1-9"), adjust the regex to ^[1-9]\d{7}$:

.matches(/^[1-9]\d{7}$/, 'Phone number must be 8 digits and cannot start with 0')

2. Non-Digit Characters in Input#

Users might accidentally enter spaces, hyphens, or plus signs (e.g., 123 45678 or +12345678). To handle this, either:

  • Sanitize the input (e.g., remove non-digits before validation), or
  • Update the regex to ignore non-digits (not recommended, as it complicates validation).

Example: Sanitizing Input (Pre-Validation)
If using Formik, sanitize the input in onChange or transform the value in Yup:

// Yup transform to remove non-digits before validation
Yup.string()
  .transform((value) => value.replace(/\D/g, '')) // Remove all non-digits
  .matches(/^\d{8}$/, 'Phone number must be exactly 8 digits (after removing spaces/hyphens)')
  .required('Phone number is required')

This allows inputs like 123-45678 (sanitized to 12345678) to pass.

3. International Numbers#

This guide focuses on 8-digit local numbers. For international numbers (e.g., with country codes), extend the regex (e.g., ^\+\d{1,3}\d{8}$ for country code + 8 digits), but that’s beyond the scope here.

Integrating with Form Libraries (e.g., Formik)#

Let’s see how to use the phoneSchema with Formik, a popular form library for React.

Example: Formik Form with Yup Validation#

import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
 
// Define the schema
const phoneSchema = Yup.object().shape({
  phone: Yup.string()
    .matches(/^\d{8}$/, 'Phone number must be exactly 8 digits')
    .required('Phone number is required'),
});
 
const PhoneForm = () => {
  return (
    <Formik
      initialValues={{ phone: '' }}
      validationSchema={phoneSchema}
      onSubmit={(values) => {
        console.log('Valid phone number:', values.phone);
        // Submit logic here
      }}
    >
      {({ isSubmitting }) => (
        <Form>
          <div>
            <label>Phone Number:</label>
            <Field type="text" name="phone" />
            <ErrorMessage name="phone" component="div" style={{ color: 'red' }} />
          </div>
          <button type="submit" disabled={isSubmitting}>
            Submit
          </button>
        </Form>
      )}
    </Formik>
  );
};
 
export default PhoneForm;

How It Behaves#

  • Empty input: Shows "Phone number is required".
  • 7 digits: Shows "Phone number must be exactly 8 digits".
  • 9 digits: Shows "Phone number must be exactly 8 digits".
  • Non-digits (e.g., 1234abcd): Shows "Phone number must be exactly 8 digits".
  • Exactly 8 digits (e.g., 12345678): Submits successfully.

Testing the Validation#

Test the schema with various inputs to ensure it works:

InputExpected Outcome
"" (empty)Fails: "Phone number is required"
1234567 (7 digits)Fails: "Phone number must be exactly 8 digits"
123456789 (9 digits)Fails: "Phone number must be exactly 8 digits"
1234abcd (8 chars, non-digits)Fails: "Phone number must be exactly 8 digits"
01234567 (8 digits, leading zero)Passes (unless leading zeros are prohibited)
12345678 (8 digits)Passes

Troubleshooting Common Issues#

1. "Schema Not Working" – Check Data Types#

Ensure the input is treated as a string. If the input is a number, leading zeros may be stripped (e.g., 01234567 becomes 1234567 as a number), causing validation to fail. Always use type="text" for phone inputs, not type="number".

2. Regex Allowing Partial Matches#

Forgetting ^ or $ in the regex (e.g., \d{8} instead of ^\d{8}$) allows partial matches (e.g., 123456789 would pass because it contains 8 digits). Always use ^ and $ to anchor the regex to the start/end of the string.

3. Conflicts with Other Validation Rules#

If combining with min() or max(), ensure they don’t override matches(). For example, min(8).max(8) works but is redundant with matches(/^\d{8}$/).

Conclusion#

Validating phone numbers for exactly 8 digits with Yup requires combining regex pattern matching (^\d{8}$) with Yup’s matches() method. This ensures the input is a string of exactly 8 digits, blocking non-digit characters and incorrect lengths.

Key takeaways:

  • Avoid min(8) alone—it allows 9+ digits.
  • Use matches(/^\d{8}$/) to enforce 8 digits only.
  • Sanitize inputs if users might enter non-digit characters (e.g., spaces, hyphens).
  • Test edge cases like leading zeros and empty inputs.

References#