\b: word boundaries, underscores and accented letters
\b asserts that one side of the current position is a word character and the other is not. Because \w is ASCII only, the assertion behaves oddly around underscores and accented letters.
At a glance
- Syntax
- \b
- Type
- zero width assertion, it consumes nothing
- Defined by
- \w on one side, a non word character or a string edge on the other
- Underscore
- a word character, so user_name has no internal boundary
- Accented letters
- not word characters, so \bcafé\b never matches
- Inside a character class
- [\b] is the backspace character U+0008
In detail
The whole definition is a comparison of the two characters either side of a position. If exactly one of them is a \w character, the position is a boundary. The start and end of the string count as non word, which is what makes a boundary appear at the front of a string that begins with a letter.
Because the definition is written entirely in terms of \w, everything odd about \w shows up here too. The underscore is a word character, so there is no boundary anywhere inside user_name, and /\bname\b/ does not find it there. That is the correct behaviour for identifiers and the wrong behaviour for almost any prose task.
Accented letters are not word characters, which produces a genuinely surprising result: /\bcafé\b/ never matches the string "café". The opening boundary is fine, but at the end the position sits between the accented e (not a word character) and the end of the string (also not a word character), so there is no transition and no boundary. Dropping the trailing \b makes it match.
Inside a character class the same escape means something entirely different. [\b] is the backspace character, U+0008, a leftover from the C escape tradition. Writing [\b\s] hoping to mean a boundary or whitespace produces a class matching backspace or whitespace, which will never match anything in ordinary text.
Examples
"the cat scattered".replace(/\bcat\b/g, "dog"); // "the dog scattered"Without the boundaries the cat inside scattered would be replaced as well.
/\bcafé\b/.test("café"); // false, é is not a word character
/\bcafé/.test("café"); // true
/[\b]/.test("\u0008"); // true, inside a class \b is backspaceThings that catch people out
- /\bcafé\b/ never matches, because the accented letter is not a word character and so no boundary exists after it. The same applies to any non-ASCII word.
- \b between two non word characters never matches, so /\b-\b/ is not a way to find a standalone hyphen and will quietly match nothing.
- [\b] is the backspace character, not a boundary assertion. Putting \b inside a character class silently changes what the pattern means.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →\B: the non word boundary and what it is good for
\B asserts that a position is not a word boundary, which turns it into an inside a word search. It also never matches at the edges of a word.
\w: the word character class, underscore included
\w is exactly [A-Za-z0-9_] in JavaScript. That includes the underscore and excludes every accented letter, which is where most \w bugs come from.
The ^ anchor: start of string, or start of line
Where the caret matches with and without the m flag, why it consumes nothing, and the entirely different job it does inside a character class.
The * quantifier: zero or more, including nothing at all
The star quantifier succeeds even when it matches nothing, which is why a global match against it returns an empty string at almost every position.
The + quantifier: one or more, and when to prefer it
The plus quantifier requires at least one repetition, which is what makes it the right default for validation where the star quietly accepts nothing.
The ? quantifier: optional, and its two other jobs
A question mark after a token makes it optional. The same character also marks a lazy quantifier and opens a special group, which is why patterns look dense.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links