94

Is there a yup function that validates a specific length?

I tried .min(5) and .max(5), but I want something that ensures the number is exactly 5 characters (ie, zip code).

reectrix
  • 7,999
  • 20
  • 53
  • 81

17 Answers17

96

This check leads to the best validation experience:

Yup.string()
.required()
.matches(/^[0-9]+$/, "Must be only digits")
.min(5, 'Must be exactly 5 digits')
.max(5, 'Must be exactly 5 digits')

output:

12f1    // Must be only digits
123     // Must be exactly 5 digits
123456  // Must be exactly 5 digits
01234   // valid
11106   // valid

Demo: https://codesandbox.io/s/yup-y6uph

SimoAmi
  • 1,696
  • 14
  • 13
  • @InamurRahman My example already includes a string of digits prefixed with zero which passes the validation as shown in the demo. Remember that we are validating strings. You cannot preserve a leading zero in a numeric value. that's not possible in any programming language. `console.log(08763)` will output `> 8763` but `console.log('08763')` will output `> 08763` – SimoAmi Dec 16 '20 at 18:50
  • 1
    I tested it with "00000" and works like a charm. Loved this solution, thank you @SimoAmi – Manjar Feb 17 '21 at 10:28
  • 1
    I love you brother for this solution – Agent K Jun 04 '21 at 05:04
  • Value should be exactly 5 not length of entered value? – Mustkeem K Jul 12 '22 at 05:55
  • @MustkeemK In the context of strings, min and max refer to the length of the input string. – SimoAmi Dec 16 '22 at 19:44
84

I don't think there's anything built in but it's easy to implement with test:

yup.string()
  .test('len', 'Must be exactly 5 characters', val => val.length === 5)

https://runkit.com/tamlyn/5ad9b99a4ba1230012d7ac21

Tamlyn
  • 22,122
  • 12
  • 111
  • 127
  • 6
    I had to add a null checker, as it is possible that the val string is null because user hasn't typed anything yet – Mese Feb 18 '19 at 15:38
  • It works like a charm. Thanks a lot. We can also make it notRequired. – Nagama Inamdar Jul 02 '19 at 10:37
  • @Tamlyn Why do we use string().test() // test method here Is that the method which should be used or there's another method which would more suit it? Somehow .test() sounds more like a testing method... – Roxy'Pro Dec 23 '19 at 12:48
  • 3
    But what with numbers where leading is 0 ? 000123.toString().length // : outputs 3 (not 6) – karolkarp Jan 10 '20 at 12:55
  • 1
    @karolkarp I don't understand what you mean. For a start `000123.toString().length` outputs `2`, not `3`. This is because the JavaScript parser interprets numeric literals starting with zero as octal numbers. But JavaScript has no internal representation for a number with leading zeros. If it has leading zeros then it must be a string. And if you don't want leading zeros on your string, normalise it `parseFloat("000123").toString().length === 3`. – Tamlyn Oct 06 '20 at 16:14
38

For future reference, if you're looking to validate a number (zip code), the above solution requires a slight tweak. The function should be :

Yup.number().test('len', 'Must be exactly 5 characters', val => val.toString().length === 5)

.length does not work on numbers, only strings.

efru
  • 1,401
  • 3
  • 17
  • 20
  • 3
    I had a warning about val being undefined. I saw Mese's comment on first answer and figured there should be a null check. So ```Yup.number().test('len', 'Must be exactly 5 characters', (val) => { if(val) return val.toString().length === 8; })``` for example. – Hylle Apr 21 '20 at 07:45
  • 1
    Thanks @Hylle. This is the real answer! – Ahmed Aziz Nov 21 '20 at 16:39
  • Also, if you can, do not store zip codes as integers. Zip codes can start with 0 (in the north east) which will chop off the leading zero. Probably best to store as varchar. – efru May 12 '21 at 17:14
  • 1
    Compensate for empty or null in 1 line: `val && val => val.toString().length === 5` – Jake Jun 14 '21 at 17:36
  • Store zip codes as strings, not numbers. Zip's sometimes start with a zero (new york) which will cut off the leading 0 if stored as an int. – efru Jul 13 '22 at 16:10
28

You can also use string.length.

yup.string().length(5)

But it doesn't work with numbers starting with zeros:

const yup = require('yup')

const schema = yup.string().length(5)

console.log(schema.isValidSync(12345)) // (true) This is valid.
console.log(schema.isValidSync(00123)) // (false) This is NOT valid.
David Ferreira
  • 1,233
  • 17
  • 24
17

@Tamlyn's answer covers the length validation aspect of the question quite well.

In the case of a zip code, you could use a regex to enforce the length and limit to numeral values within the Yup.string() (you wouldn't want to use a Yup.number() type as it wouldn't support zip codes starting with a zero 0####)

// ##### format zip code
Yup.string().matches(/^[0-9]{5}$/, 'Must be exactly 5 digits')

// ##### and #####-#### format zip codes
Yup.string().matches(/^[0-9]{5}(?:-[0-9]{4})?$/, 'Must be 5 or 9 digits')
dtesta
  • 171
  • 1
  • 4
  • Hi dtesta! Thank you for your solution. Your code which is: Yup.string().matches(/^[0-9]{5}$/, 'Must be exactly 5 digits') works but I also need to check for the minimum and maximum so what I did was to user .min(10, 'Must be exactly 10-12 digits.') and .max(12, 'Must be exactly 10-12 digits.'). – Jeanne vie Mar 09 '20 at 09:09
9

Works like a charm for type number.

yup.number().test('len', 'Max 6 numbers', (val) => val.toString().length <= 6)
Brayan Loayza
  • 678
  • 7
  • 15
6

To add to the other answers, none of them are checking that a value exists (I see some have mentioned this in comments after posting this though)...

If it is not present and the field is left empty, it will be trying to get the length of undefined or null which will then give you a javascript error and prevent other conditions such as .required() from working (if you had it setup like of course).

This would probably be slightly better:

// Check we have a value as well
Yup.number().test('len', 'Must be exactly 5 characters', val => val && val.toString().length === 5 )
Matt Royal
  • 335
  • 3
  • 6
  • 1
    Using `number()` doesn't work as you could be entering a security code that starts with zeros and Yup.number() swallows them. Example: `000445` would be passed on to `val` as `445`. – SimoAmi Feb 21 '20 at 19:40
5

Try this:

Yup.number()
.required()
.min(10000, 'Must be exactly 5 characters')
.max(99999, 'Must be exactly 5 characters')
.label("Zip Code"),
Harshal
  • 7,562
  • 2
  • 30
  • 20
  • 1
    This answer highlights the fact that min and max work differently for string() and number(). – technoY2K Aug 12 '20 at 21:59
  • 7
    This does not work. What about zip codes that begin with `0`? You cannot make a zip code field a number nor validate it is between two numbers. – Mike Dec 15 '20 at 20:58
3

You can also still use the validation for number but when using test for validation of length you will have convert it to a string before testing it.

Yup.object().shape({
  zipCode: Yup.number()
    .required('Zip code is a required field')// optional
    .typeError('Zip code can only be a number')// optional as well
    .test('len', 'Zip code needs to be excatly 5 digits', val => val.toString().length === 5)
});
Artan M.
  • 817
  • 1
  • 11
  • 16
2

The test API runs into issues with ReactJs when your field has no value. You can use the length API instead

Yup.string().length(4, 'This field has to be exactly 4 characters!')
BoyePanthera
  • 546
  • 5
  • 10
1

matches(/^[0-9]{8}$/, "Only 8 digits") Now, the Yup will show error if the user input letter and more or less 8 digits

0

@efru's answer works great for numbers that are less than 22 characters. However val.toString().length does not work for numbers larger than 22 characters. The reason for this is that larger numbers are converted to exponential format when converted to a string in javascript.

The solution I found that works best is:

Yup.number().test('len', 'Must be exactly 25 characters', val => Math.ceil(Math.log10(val + 1)) === 25)

tschumann
  • 11
  • 2
0
import { string, date} from 'yup' // Take out what is needed in import

string()
.trim()
.matches(
  /^[0-9]{4}[0-9]{2}[0-9]{2}T 0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z$/,
  'createdOn is not in correct format',
)
.max(24),
Dharman
  • 30,962
  • 25
  • 85
  • 135
sid7747
  • 690
  • 7
  • 15
  • 4
    Hello! While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Brian61354270 Jul 30 '20 at 14:00
0

i convert my number to string and store my number in a react state as a string.

my solution was this

    amount: Yup.number()
      .test('len', maxAmountLength, () => amountState.length <= 50)
Evaldas
  • 169
  • 7
0

You can use typeError like following:

yup.number()
    .typeError('Must be only digits')
    .test('len', 'Must be exactly 5 characters', val => val.length === 5)
AbdurrahmanY
  • 809
  • 13
  • 22
0

You can convert the number to a string and check its length.

Yup.number()
.required("Required")
.test("min-length", "Please enter a valid number", (value) => {
  // Convert the number to a string and check its length
  const stringValue = String(value);
  return stringValue.length == 10;
}),
Abhith
  • 51
  • 4
-3

This is a simple solution, but it is not perfect.

Yup.string()
.required()
.min(5, 'Must be exactly 5 digits')
.max(5, 'Must be exactly 5 digits')

One of the solutions is:

Yup.string().matches(/^[0-9]{5}$/, 'Must be exactly 5 digits')
S. Hesam
  • 5,266
  • 3
  • 37
  • 59