completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Character classes & sets

Learn how JavaScript regex character classes match ASCII digits, words, spaces, Unicode properties, v-flag sets, and safe user input.

By the end, you can
  • 01
    Read the built-in shorthandsSay exactly which characters \d, \w, \s, their capital opposites, and the dot match.
  • 02
    Choose sets or Unicode propertiesWrite ranges, flipped sets with ^, and properties such as \p{L}, \p{N}, \p{Script=Greek}, and \P{...}.
  • 03
    Build a regex from user text safelyEscape special characters with a helper or RegExp.escape, and know where the v flag is stricter.

What a character class is

A regular expression, or regex, reads text one character at a time. Each part of the pattern stands for one spot in the text. A character class says which characters are allowed in one spot. For example:

  • \d allows one digit.
  • [abc] allows one a, one b, or one c.
  • [^abc] allows one character that is not a, b, or c.
  • \p{L} allows one letter from any language.
Definition

A character class is a part of a regex that matches exactly one character. It lists which characters are allowed. You can write that list in four ways:

  • a short code, called a shorthand, such as \d;
  • characters in square brackets, called a bracket set, such as [a-z];
  • a Unicode property, such as \p{L};
  • sets combined with the v flag, such as [[a-z]--[aeiou]].

The previous lesson, Patterns & flags, showed how to write a regex, add flags, and use test, match, and replace. This lesson answers one question: which characters does one spot allow? To review text in JavaScript, see strings and Unicode & strings.

The table compares four tools that are easy to mix up. Read each row from left to right.

Four tools that are easy to mix up
You writeWhat it allowsWhen to use it
\wOne basic English letter (A-Z, a-z), one digit (0-9), or _.Use it for simple IDs and fixed formats.
\p{L}One letter from any language. It needs the u or v flag.Use it for names and text in many languages.
u flagReads one whole character at a time, even an emoji. It also turns on \p{...}.You need it for \p{...} and for an emoji to count as one character.
v flagEverything u does, plus &&, --, sets inside sets, and string properties.Use it when you combine sets or match \p{RGI_Emoji}.

Built-in classes: \d, \w, \s, and dot

STEP THROUGH

JavaScript has short codes for common sets. Each one matches one character. The first two know only the basic English characters. This basic set is called ASCII.

  • \d matches one digit from 0 to 9. It does not match digits from other writing systems, such as the Arabic digit three, ٣.
  • \w matches one word character: a letter from A to Z or a to z, a digit, or the underscore _. It does not match letters with accents, such as é.
  • \s matches one space-like character: a space, a tab, a line break, or a rarer space such as the non-breaking space (a space that keeps two words on the same line).
  • The dot . matches almost any one character. It skips line breaks.

A capital letter flips the meaning. \D matches one character that is not a digit. \W and \S work the same way.

Real-life analogyA door guard with a guest list

Picture a guard at a door. The guard checks one guest at a time against a printed list. The guard does not look at the whole line of people. The guard looks only at the next guest.

In real life: The guard holds one printed guest list
In JavaScript: A character class is one list of allowed characters
In real life: Each guest is either on the list or not
In JavaScript: Each character either matches \d, \w, or your set, or it does not
In real life: A different door can have a different list
In JavaScript: Changing \w to \p{L} changes which characters are allowed

The step-through below runs real checks. Pick a group of classes, then look for these results:

  • \d skips ٣, but \p{N} (any number) matches it.
  • \w matches the underscore _, but not é.
  • The dot skips a line break, unless you add the s flag.
  • An emoji such as 😄 is stored as two pieces in a string. Without the u or v flag, the dot matches each piece, so you get two matches. With u, you get one.
Step through the built-in classes
Step 0 of 5Ready
Your turn: follow the blue line

Pick a group of classes, guess which samples match, then step through the real checks.

Running in
  1. script
Next: line 1
  ["ASCII 9", "9"],  ["Arabic ٣", "٣"],  ["A", "A"],  ["underscore", "_"],  ["space", " "],]; const labels = (regex) => samples  .filter(([, value]) => regex.test(value))  .map(([label]) => label)  .join(", "); console.log(labels(/\d/u));console.log(labels(/\D/u));console.log(/\p{N}/u.test("٣"));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose a group of classes to step through

A new choice starts over, with the code for that group.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Left: the regex /\d/u checks two cards, 9 and the Arabic digit three. 9 is a match, the Arabic three is no match. Right: the regex /\p{N}/u checks the same two cards, and both are a match./\d/u9٣matchno match/\p{N}/u9٣matchmatch
\d takes only the digits 0 to 9, so it skips ٣. \p{N} takes a number from any writing system, so it takes both.

Next, try each class on one sample string. Pick a class and see which characters it matches.

Pick a class and check each character
The code for the chosen classPop out in the code editor (opens in a new tab)JavaScript
const sample = "A é β 9 ٣ _ -\u00A0.\n☕ 😀";const matches = sample.match(/\d/gu) ?? [];console.log(matches.join(" | "));
Matches\d

Match list: 9

Askips
characterA
spaceskips
characterspace
éskips
characteré
spaceskips
characterspace
βskips
characterβ
spaceskips
characterspace
9matches
character9
spaceskips
characterspace
٣skips
character٣
spaceskips
characterspace
_skips
character_
spaceskips
characterspace
-skips
character-
NBSPskips
characterNBSP
.skips
character.
\nskips
character\n
☕skips
character☕
spaceskips
characterspace
😀skips
character😀
Step 1 of 19\d matches

One digit from 0 to 9 only. The match list comes from running the regex shown in the code.

The sample has accented and Greek letters, an Arabic digit, a normal space and a non-breaking space, punctuation, a line break, and two emoji for the emoji class.
\w is not a letter class

Think of \w as basic English letters, digits, and _. With the i and u flags together, it also matches two rare symbols that look like K and s. It still does not match letters such as é or β. When you mean "letters from any language", use \p{L}.

Sets and ranges: [abc], [a-z0-9], and [^...]

SORT IT

Square brackets make your own set. The set still matches only one character.

  • [abc] matches one a, one b, or one c.
  • A hyphen between two characters makes a range. [A-Z] means any one capital letter from A to Z. [A-Z0-9] means one capital letter or one digit.
  • A caret ^ right after [ flips the set. [^A-Z0-9] means one character that is not a capital letter or a digit.
Six small sets and what they findPop out in the code editor (opens in a new tab)JavaScript
console.log("cab".match(/[abc]/g).join(""));console.log("B7!".match(/[A-Z0-9]/g).join(""));console.log("B7!".match(/[^A-Z0-9]/g).join(""));console.log("a-z".match(/[a\-z]/g).join(""));console.log("a-z]^".match(/[a-z\]\^-]/g).join("|"));console.log("a1!".match(/[^a-z0-9]/g).join(""));

In plain words, line by line:

  1. Line 1: [abc] matches c, a, and b, so it prints cab.
  2. Line 2: [A-Z0-9] matches the capital B and the digit 7. It prints B7.
  3. Line 3: the caret flips the set. Only ! is not a capital letter or a digit, so it prints !.
  4. Line 4: \- is a hyphen with a backslash before it, so it means a real -, not a range. The set is a, -, or z, and it prints a-z.
  5. Line 5: the set holds the range a-z, a real ] written \], a real ^ written \^, and a real - at the end. Every character matches, so it prints a|-|z|]|^.
  6. Line 6: [^a-z0-9] keeps only what is not a small letter or a digit. It prints !.
Two rows. Row 1: the set [A-Z0-9] looks at the cards B, 7, and ! and gives "B7". Row 2: the set [^A-Z0-9] looks at the same cards and gives "!".[A-Z0-9]B7!"B7"[^A-Z0-9]B7!"!"
[A-Z0-9] allows one capital letter or one digit, so it finds B and 7. The ^ in [^A-Z0-9] flips the set, so it finds only !.

To escape a character means to put a backslash \ before it, so it stands for itself. The rules are different inside and outside the brackets:

  • Outside brackets, many characters have a special meaning: ., +, ?, *, (, ), {, }, |, ^, $, and \.
  • Inside brackets, only a few need care: ] (it closes the set), \, ^ at the very start, and - between two characters.
  • For a real hyphen, put - first or last in the set, or write \-.
Which kind of class is it?
  • \d
  • [a-z0-9]
  • \p{L}
  • \p{Script=Greek}
  • [\p{L}--[aeiou]]
  • \p{RGI_Emoji}
Try it yourself
0 of 6 correct

Sort each pattern by the kind of class it is. Each one matches a single character.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Unicode properties with \p{...} and \P{...}

STEP THROUGH

Unicode is the big list of characters from every writing system. It gives each character labels, called properties, such as "letter", "number", or "Greek". A regex can match by property:

  • \p{...} (small p) matches one character that has the property.
  • \P{...} (capital P) matches one character that does not have it.
  • Both need the u or v flag.

Some useful properties:

  • \p{L}: any letter.
  • \p{Lu}: any capital letter.
  • \p{N}: any number.
  • \p{Script=Greek}: any character from the Greek alphabet.
  • \p{Emoji_Presentation}: characters that show as an emoji picture by default.
Step through Unicode properties
Step 0 of 5Ready
Your turn: follow the blue line

Unicode properties need the u or v flag. Pick a group, then step through to see which characters match.

Running in
  1. script
Next: line 1
 console.log(text.match(/\p{L}/gu).join(" "));console.log(text.match(/\p{N}/gu).join(" "));console.log(text.match(/\P{L}/gu).filter((ch) => ch !== " ").join("|"));console.log(/^\p{L}$/u.test("e\u0301"), /^\p{M}$/u.test("\u0301"));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose a group of properties to step through

Properties need the u or v flag. The emoji choice uses emoji because it is about emoji properties.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

A property only describes characters. It does not choose your rules for you. For example:

  • For names from many countries, you may need letters, accent marks, spaces, apostrophes, and hyphens.
  • For usernames, you may want only basic English letters and digits, on purpose.

Write the regex for the rule you chose. The last line of the lab shows why. e\u0301 is the letter e followed by a separate accent mark. On screen it looks like é, but it is two characters. So it is not one \p{L} letter. The accent alone matches \p{M} (any mark).

The v flag: set operations and string properties

INTERACTIVE

The v flag is a newer, stronger version of the u flag. It adds four things:

  • -- takes one set away from another. This is called set difference.
  • && keeps only characters that are in both sets. This is called intersection.
  • A set can hold other sets in brackets, such as [[a-z]--[aeiou]].
  • Some properties match a whole emoji made of several characters as one match. These are called string properties.

To check whether a regex uses v, read its unicodeSets property. It is true or false, and you can only read it, not change it.

The set \p{L} (all letters), minus the set [aeiou] (vowels). Used on the text "alphabet β123", it gives "lphbtβ".\p{L}--[aeiou]lettersvowels"alphabet β123""lphbtβ"
With the v flag, -- takes one set away from another. All letters minus a e i o u leaves lphbtβ.
Combine sets with the v flag
The code for the chosen examplePop out in the code editor (opens in a new tab)JavaScript
const text = "alphabet β123";const consonantsAndGreek = text.match(/[\p{L}--[aeiou]]/gv) ?? [];console.log(consonantsAndGreek.join(""));
Outputdifference --
console.log 1what it printed
printslphbtβ
Step 1 of 4difference --

[\p{L}--[aeiou]] means every letter except a, e, i, o, and u. The Greek β stays, because it is a letter and not one of those vowels.

Every example here needs the v flag. The string property example uses emoji, because emoji are what string properties match today.

In the string property choice, the last line prints true, because new RegExp("", "v").unicodeSets is true for a regex made with v. Older browsers do not know the v flag. To check, make a tiny v regex inside a try block. If it throws an error, use a simpler u pattern instead.

The v flag gives new meaning to some symbols inside brackets, so it is stricter about escaping. For example, a real | inside a v set must be written \|. The next code throws an error on purpose. Read the error, then fix it to [a\|b].

This throws: | needs a backslash with vPop out in the code editor (opens in a new tab)JavaScript
// In v mode, | is reserved inside a character class.new RegExp("[a|b]", "v");

Escaping user input and RegExp.escape

SAFE DYNAMIC REGEX

When a learner types a search term, that text is data. If you pass it straight to new RegExp(term), symbols such as +, [, and ? turn into regex rules. For example, a+b would mean "one or more a, then b". The fix is to escape the term first, then build the regex.

The user typed a+b. Row 1: used as is, the regex /a+b/ matches aaab, which is wrong. Row 2: escaped, the regex /a\+b/ matches only the text a+b.term = "a+b"used as is/a+b/matches aaabescaped/a\+b/matches only a+b
Used as is, + means "one or more", so /a+b/ also finds aaab. After escaping, \+ is a plain plus sign.
A helper that escapes user textPop out in the code editor (opens in a new tab)JavaScript
function escapeForRegExp(input) {  return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");} const userText = "a+b [test]";const escaped = escapeForRegExp(userText);const regex = new RegExp(escaped, "g"); console.log(escaped);console.log("find a+b [test] here".replace(regex, "<mark>$&</mark>"));

In plain words, line by line:

  1. Lines 1 to 3 make the helper escapeForRegExp. Line 2 finds every special character and puts a backslash before it. In the replacement, $& means "the character that was found".
  2. Line 6 escapes the user text. Line 7 builds the regex from the escaped text.
  3. Line 9 prints the escaped text, a\+b \[test\].
  4. Line 10 finds the real text a+b [test] and wraps it in <mark> tags.

RegExp.escape is a newer built-in method from ES2025, the 2025 version of JavaScript. It does the same job and handles more unusual cases. MDN's browser support data lists it in Chrome 136+, Firefox 134+, Safari 18.2+, and Node.js 24+. This site's tests run on Node 22, which does not have it. So Node 22 code still needs the helper above, or a polyfill (code that adds a missing feature).

The built-in RegExp.escapePop out in the code editor (opens in a new tab)JavaScript
const userText = "a+b [test]";const escaped = RegExp.escape(userText);const regex = new RegExp(escaped, "g"); console.log(escaped);console.log(regex.test("find a+b [test] here"));

Line 5 prints \x61\+b\x20\[test\]. RegExp.escape also escapes the first letter and the space, as \x61 and \x20. That looks odd, but it keeps the text safe wherever you put it in a regex. Line 6 prints true.

Where character classes are used

Character classes show up when you check input, clean up text, search, and split text into pieces. First write the rule in plain words, such as "basic English username" or "digits from a phone field". Then pick the class that says exactly that.

Each row starts with a real task. Read across to see a pattern and why it fits.

Choosing a class for a real task
The taskA pattern to start withWhy it fits
Check a username/^[A-Za-z0-9_-]+$/ or a version with \p{L}First decide if your rule allows only basic English letters. Then pick the pattern.
Keep only phone digitsvalue.replace(/\D/g, "")It removes everything that is not 0 to 9. Use \p{N} only if you accept digits from other writing systems.
Check a name from any country/^[\p{L}\p{M}' -]+$/uIt allows letters, accent marks, apostrophes, spaces, and hyphens. Your app may still need more rules.
Highlight a search termnew RegExp(RegExp.escape(term), "gi")The user's text is read as plain text, not as regex rules.
  • Usernames: ^[A-Za-z0-9_-]+$ fits when you want only basic English letters, digits, _, and -.
  • Phone digits: value.replace(/\D/g, "") removes everything that is not a digit from 0 to 9.
  • Removing punctuation: text.replace(/[^\p{L}\p{N}\s]/gu, "") keeps letters, numbers, and spaces.
  • Names from many countries: combine \p{L}, accent marks, spaces, and punctuation, but only the ones your app accepts.
  • Search highlighting: escape the user's term, then build the regex.

Common misconceptions

  • "\d means any digit." It means only 0 to 9. Use \p{N} for numbers from every writing system.
  • "\w means any letter." It means basic English letters, digits, and _. It is not a good check for names from many countries.
  • "The dot matches everything." It skips line breaks unless you add the s flag. Without u or v, it matches only half of an emoji such as 😄.
  • "A bracket set matches a word." A set matches one character. The next lesson, on quantifiers, shows how to repeat it.
  • "The v flag is just u with a new name." It adds --, &&, sets inside sets, string properties, and stricter escaping.
  • "Text from a text box is safe to put in a regex." Text can contain regex symbols. Escape it before you build a pattern.

The table compares three tools. Read each row to see when to use the tool and when not to.

Similar tools for different jobs
You writeUse it whenDon't use it when
\wYour rule allows only basic English letters, digits, and _.You mean letters from many languages.
\p{L}You mean letters from any language, and you use u or v.You need a strict format with only basic English letters.
uYou need Unicode properties and whole emoji to work.You need &&, --, sets inside sets, or string properties. Use v for those.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict which digits \d keeps

Type the exact first line this code prints.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const values = ["9", "٣"];
console.log(values.filter((value) => /\d/u.test(value)).join("|"));
console.log(/\p{N}/u.test("٣"));

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeChoose a class for letters from any language

    This username regex uses \w, so it allows only basic English letters. It prints just maria. Which class should replace \w when the rule is "letters from many languages"?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const names = ["maria", "maria-é", "δοκιμή"];
    const asciiOnly = /^[\w-]+$/u;
    console.log(names.filter((name) => asciiOnly.test(name)).join(","));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticePredict what the fixed filter prints

      Type the exact string the fixed filter prints.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const names = ["maria", "maria-é", "δοκιμή"];
      const international = /^[\p{L}\p{N}_-]+$/u;
      console.log(names.filter((name) => international.test(name)).join(","));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRead a set difference with v

        Predict what this v-flag set difference prints.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const text = "a b c x y z";
        console.log(text.match(/[[a-z]--[aeiou]]/gv).join(""));

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengePick the method that makes user text safe

          A user searches for a+b. Which ES2025 method should you call before you build the regex?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const term = "a+b";
          const unsafe = new RegExp(term);
          console.log(unsafe.test("aaab"));

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            8 QUESTIONS
            Lesson quiz · 8 questionsScore: first tries count
            1. Question 1 of 8What does \d match in JavaScript?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does the word-class snippet print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const values = ["é", "_", "A"];
              console.log(values.filter((value) => /\w/u.test(value)).join(","));

              Choose an answer to see the explanation.

            3. Question 3 of 8What does this emoji example print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("😄".match(/./g).length, "😄".match(/./gu).length);

              Choose an answer to see the explanation.

            4. Question 4 of 8What does this range code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("aB7-".match(/[a-z0-9]/g).join(""));

              Choose an answer to see the explanation.

            5. Question 5 of 8What does this Greek check print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log("A β Ж".match(/\p{Script=Greek}/gu).join(""));

              Choose an answer to see the explanation.

            6. Question 6 of 8What does this v-flag set difference print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const text = "a b c x y z";
              console.log(text.match(/[[a-z]--[aeiou]]/gv).join(""));

              Choose an answer to see the explanation.

            7. Question 7 of 8Why should a search highlighter escape the user's text before new RegExp?

              Choose an answer to see the explanation.

            8. Question 8 of 8Which statement about RegExp.escape is true?

              Choose an answer to see the explanation.

            Key takeaways

            • \d and \w know only basic English characters. \s also matches rarer spaces.
            • The dot skips line breaks unless you add s. It needs u or v to match an emoji such as 😄 as one character.
            • A bracket set matches one character. Ranges and ^ decide which characters are allowed.
            • Unicode properties such as \p{L}, \p{Lu}, \p{N}, and \p{Script=Greek} need u or v.
            • The v flag adds --, &&, sets inside sets, string properties, and stricter escaping.
            • Escape user text with a helper or RegExp.escape before you build a regex from it.

            Remember the one-liner.
            First say in plain words which characters you mean. Then choose the regex that matches exactly those characters.

            Up next: Quantifiers. They let the class you chose repeat, with +, *, ?, and {n,m}.

            CompleteFrontend Clear concepts. Working examples.