Is [\s\s] Valid With JavaScript’s v Flag?
A one-letter case change turns a whitespace test into an any-character test. With v enabled, both patterns compile, so syntax alone cannot tell you which one you meant.
[\s\s]is valid with JavaScript’svflag, but it is redundant: it matches one whitespace character, while[\s\S]matches any one character.
The Short Answer: Yes, but It Is Redundant
Inside a character class, adjacent members are combined into one set. The pattern [\s\s] contains the set represented by \s twice:
whitespace ∪ whitespace = whitespace
Repeating the same set does not add another possibility. /[\s\s]/v therefore behaves like /\s/v.
Capitalization changes the second escape completely. Lowercase \s means whitespace, while uppercase \S means every character outside that whitespace set. Combining them covers both sides:
whitespace ∪ non-whitespace = every character
Start with anchored tests so that each pattern must match the entire one-character input:
const whitespaceTwice = /^[\s\s](?![\s\S])/v;
const everyCharacter = /^[\s\S](?![\s\S])/v;
console.log(whitespaceTwice.test(" "));
console.log(whitespaceTwice.test("A"));
console.log(everyCharacter.test(" "));
console.log(everyCharacter.test("A"));
console.log(everyCharacter.test("\n"));
console.log(whitespaceTwice.test(" \n"));
console.log(everyCharacter.test("A\n"));
true
false
true
true
true
false
false
The lowercase pattern accepts the space and rejects A. The mixed-case pattern accepts the space, A, and the newline.
That is the whole distinction. [\s\s] is valid but says the same thing twice; [\s\S] is valid and covers every character.
What \s, \S, and Square Brackets Mean
In the ordinary character-only classes used here, a character class describes a set of characters that may occupy one position in a match. Square brackets create the class, and members such as \s and \S add character sets. In v mode, some classes can also contain finite strings.
This class accepts either a, b, or c:
const letter = /^[abc]$/v;
console.log(letter.test("a"));
console.log(letter.test("c"));
console.log(letter.test("d"));
true
true
false
The members do not describe a three-character sequence. [abc] consumes one character chosen from the set, while abc outside brackets consumes the three-character sequence.
Character class escapes add predefined sets instead of individual characters:
\sdenotes the characters covered by ECMAScript’sWhiteSpaceandLineTerminatordefinitions.\Sdenotes the complement of\s, meaning every character not in that set.[\s\S]combines both sets by union.[\s\s]combines the whitespace set with the same whitespace set.
The capitalization is part of the syntax. s and S are not interchangeable labels.
The same union rule applies when literal characters and escapes appear together. [A\s] accepts either uppercase A or one whitespace character. It does not require A followed by whitespace.
The broad Character classes guide covers predefined escapes, negated classes, and other class forms. Sets and ranges takes the range syntax further. Here, the important point is smaller: adjacent class operands are alternatives, and duplicate alternatives remain legal.
One more boundary matters. [\s\S] consumes one character. It does not consume an arbitrary amount of text until you add a quantifier:
const oneCharacter = /^[\s\S](?![\s\S])/v;
const anyNumberOfCharacters = /^[\s\S]*$/v;
console.log(oneCharacter.test("A\nB"));
console.log(oneCharacter.test("A\n"));
console.log(anyNumberOfCharacters.test("A\nB"));
console.log(anyNumberOfCharacters.test(""));
false
false
true
true
* allows zero or more repetitions of the class. Use + when at least one character is required.
Why Both Patterns Remain Valid With v
The v flag enables Unicode sets mode. It is a Unicode-aware regular expression mode with richer character class syntax, including nested classes, intersection, subtraction, and string alternatives written with \q{...}.
These features extend the ordinary class rules. They do not make duplicate operands illegal.
Plain adjacency still forms a union:
[AB] A or B
[\s\S] whitespace or non-whitespace
[\s\s] whitespace or whitespace
The first two unions expand the accepted set. The third does not, but a redundant set operation is still a valid set operation. The ECMAScript pattern grammar accepts it.
Unicode sets mode also provides two explicit operators:
&&keeps characters present in both operands. This is intersection.--removes the right operand from the left operand. This is subtraction.
Say you need an ASCII letter. \p{ASCII} includes ASCII characters, and \p{Letter} includes characters with the Unicode letter property. Their intersection keeps characters belonging to both sets:
const asciiLetter = /^[\p{ASCII}&&\p{Letter}]$/v;
console.log(asciiLetter.test("R"));
console.log(asciiLetter.test("7"));
console.log(asciiLetter.test("é"));
true
false
false
R belongs to both sets. 7 is ASCII but not a letter, and é is a letter but not ASCII.
Subtraction works in the other direction. [\w--_] begins with \w and removes the underscore:
const wordWithoutUnderscore = /^[\w--_]$/v;
console.log(wordWithoutUnderscore.test("K"));
console.log(wordWithoutUnderscore.test("4"));
console.log(wordWithoutUnderscore.test("_"));
true
true
false
These operators matter when one set must be narrowed. [\s\s] needs neither because it is an ordinary union.
The u and v flags are alternative Unicode-aware modes. They cannot appear together on one regular expression. Use v when the pattern needs Unicode sets syntax; RegExp v and d Flags covers the flag alongside match indices, while Unicode regular expressions explains Unicode property escapes.
Once regular expressions become named validators rather than isolated tests, Patterns and Architecture shows how to keep matching rules separate from the application decisions that use them.
Valid and Invalid v-Mode Classes
Unicode sets mode gives punctuation inside a class more responsibility. Some characters introduce grouping or set operations, so text that worked as an unescaped class member in another mode may be a syntax error with v.
This table separates the common cases:
| Pattern or source | Result | Reason |
|---|---|---|
[\s\s] | Valid | It unions the whitespace set with itself. |
[\s\S] | Valid | It unions whitespace with its complement. |
[\w--_] | Valid | It subtracts _ from \w. |
[\p{ASCII}&&\p{Letter}] | Valid | It intersects the ASCII and letter sets. |
[A-\s] | Invalid | A character class escape cannot act as that range endpoint. |
[(] | Invalid | ( is reserved class syntax in v mode and must be escaped. |
[!!] | Invalid | The doubled punctuator is reserved inside a v-mode class. |
[\p{ASCII}&&\p{Letter}--[A-Z]] | Invalid | Intersection and subtraction cannot be mixed at the same class level. |
new RegExp("[\s\S]", "v") | Valid, but wrong | JavaScript parses the string first and removes the intended regex backslashes. |
The last row is a different kind of failure. The regular expression still compiles, but the constructor receives [sS], not [\s\S]. It matches a lowercase or uppercase s.
In v-mode classes, (, ), [, ], {, }, /, -, \, and | participate in or are reserved for class syntax. Escape one when you intend the literal character rather than its syntactic role. Escaping, special characters covers the two places escaping can happen.
Repeated punctuators can also be reserved. [!!] is not a shortcut for one or two exclamation marks. Escape the literal characters, or move them outside the class when a sequence is what you need.
Mixed set operators need nesting so that the order is explicit. This version first finds ASCII letters, then removes uppercase ASCII letters:
const lowercaseAsciiLetter =
/^[[\p{ASCII}&&\p{Letter}]--[A-Z]]$/v;
console.log(lowercaseAsciiLetter.test("m"));
console.log(lowercaseAsciiLetter.test("M"));
console.log(lowercaseAsciiLetter.test("é"));
true
false
false
The inner class completes the intersection. The outer class then performs the subtraction.
This stricter grammar catches ambiguous punctuation, but it does not reject repetition merely because the repetition adds nothing. Duplicate operands and malformed operators are different cases.
Regex Literals, Constructors, and HTML pattern
A regex literal goes straight to the regular expression parser. Write the backslashes once:
const fromLiteral = /[\s\S]/v;
console.log(fromLiteral.test("\n"));
true
The RegExp constructor adds a JavaScript string layer. JavaScript parses the string before the regex parser receives it, so each intended regex backslash must be doubled:
const fromLiteral = /[\s\S]/v;
const fromConstructor = new RegExp("[\\s\\S]", "v");
console.log(fromLiteral.source);
console.log(fromConstructor.source);
console.log(fromConstructor.test("\n"));
[\s\S]
[\s\S]
true
Both regular expressions receive the same pattern. Without doubled backslashes, new RegExp("[\s\S]", "v") receives [sS] and tests for either form of the letter s.
HTML’s pattern attribute adds another place where these rules appear. The HTML Standard creates that regular expression using Unicode sets mode.
When a value is present, this pattern accepts one or more characters:
pattern="[\s\S]+"
The pattern attribute does not reject an empty value. When emptiness must also be rejected, use required:
<input required pattern="[\s\S]+">
This is HTML attribute text, not a JavaScript string, so the backslashes appear once. The + belongs outside the class because it repeats the complete [\s\S] operand.
The practical split is short:
- Regex literal:
/[\s\S]/v - JavaScript constructor:
new RegExp("[\\s\\S]", "v") - HTML attribute value:
pattern="[\s\S]+"
Same regular expression escape, different surrounding language.
Which Form Should You Use?
Use \s when one whitespace character is the requirement. Writing [\s\s] is valid, but the duplicate operand hides the rule you mean.
Use [\s\S] when you specifically need an any-character class, especially inside a larger class expression. Add *, +, or another quantifier when the pattern must consume more than one character.
For modern dot-all intent, . with the s flag is usually clearer:
const whitespace = /^\s$/v;
const textByClass = /^[\s\S]*$/v;
const textByDot = /^.*$/sv;
console.log(whitespace.test("\n"));
console.log(textByClass.test("draft\nready"));
console.log(textByDot.test("draft\nready"));
true
true
true
The flags do separate jobs. s lets dot match line terminators, v enables Unicode sets mode, and m changes how ^ and $ behave around line boundaries. Multiline mode of anchors covers that last rule.
[\s\S] remains valid. The correction is not to ban it, but to preserve the capital S when every character is the intended set.