Word boundary: \b

Some parts of a regexp don’t match characters at all. They match a position. The word boundary pattern:\b belongs to this family, right alongside pattern:^ (start of string) and pattern:$ (end of string). These are called anchors or zero-width assertions: they consume nothing, they only ask a yes/no question about where you currently stand in the string.

When the regexp engine — the piece of the JavaScript runtime that actually walks through your string looking for a match — reaches a pattern:\b, it doesn’t read a character. It looks at the gap between two characters and asks: “Is this spot the edge of a word?”

What counts as a boundary

A word boundary sits at exactly three kinds of position:

  • String start, when the first character is a word character pattern:\w.
  • Between two characters, when one side is a word character pattern:\w and the other side is not.
  • String end, when the last character is a word character pattern:\w.

Remember what pattern:\w covers: a Latin letter a-z or A-Z, a digit 0-9, or an underscore _. Everything else — spaces, commas, exclamation marks, hyphens — is a “non-word” character. A boundary is any place where you flip from one kind to the other.

Hi,·JS
Violet ▏ marks each \b. Word chars are dark; the comma and space (shown as ·) are muted non-word chars.
A boundary lives in the gap between a word character and a non-word character (or a string edge).

Take the regexp pattern:\bRust\b. It finds subject:Rust inside subject:Howdy, Rust! because subject:Rust stands alone there. But it finds nothing in subject:Howdy, Rusty!, because after subject:Rust the next character is subject:y, a word character — no boundary, no match.

alert( "Howdy, Rust!".match(/\bRust\b/) );   // Rust
alert( "Howdy, Rusty!".match(/\bRust\b/) );  // null

Tracing a boundary by hand

Let’s map every boundary in subject:Howdy, Rust!. Walk the string and mark each spot where a word character meets a non-word character or a string edge.

1Howdy2,·3Rust4!
1: start→H · 2: y→comma · 3: space→R · 4: t→exclamation. The space is shown as ·.
Boundary positions in 'Howdy, Rust!' — numbered gaps 1 through 4.

Notice where boundaries do not appear. There’s no boundary between subject:H and subject:o, because both are word characters. There’s none between the comma and the space either, since neither is a word character. And crucially, after the final subject:! there is no boundary, because subject:! isn’t a word character.

Now the pattern pattern:\bHowdy\b matches, and here’s the play-by-play:

  1. At the very start, pattern:\b passes — the first character subject:H is a word character (that’s boundary 1).
  2. The literal pattern:Howdy matches the next five characters.
  3. pattern:\b passes again — we’re sitting between subject:y and the comma (boundary 2).

That’s a clean match. Contrast it with two patterns that fail:

  • pattern:\bHowd\b fails. After matching subject:Howd, the engine tests pattern:\b between subject:d and subject:y. Both are word characters, so there’s no boundary there.
  • pattern:\bRust!\b fails. It matches subject:Rust! fine, but then demands a boundary after subject:!. Since subject:! is a non-word character, no boundary exists after it.
alert( "Howdy, Rust!".match(/\bHowdy\b/) ); // Howdy
alert( "Howdy, Rust!".match(/\bRust\b/) );  // Rust
alert( "Howdy, Rust!".match(/\bHowd\b/) );  // null (no match)
alert( "Howdy, Rust!".match(/\bRust!\b/) ); // null (no match)

The fastest way to build intuition for pattern:\b is to watch it highlight. Edit the pattern and the test string below and see exactly which slices match. Try removing one pattern:\b, or wrapping a word that touches other letters, and notice how the highlights move.

interactiveLive \b match highlighter

Boundaries work with digits too

Since pattern:\w includes digits, pattern:\b is just as happy sitting next to numbers. This makes it a neat tool for grabbing standalone numbers of an exact length.

The pattern pattern:\b\d\d\b finds two-digit numbers that stand on their own — bounded by spaces, punctuation, or the string edges, but never glued to more digits.

alert( "7 40 512 63".match(/\b\d\d\b/g) ); // 40,63
alert( "81,92,03".match(/\b\d\d\b/g) );    // 81,92,03

Why does the first line skip subject:7, subject:512, and match only subject:40 and subject:63? Walk it through:

7one digit only — \d\d needs two
40two digits, space on each side — match
512after matching 51, no \b before 2 — reject
63two digits, space before and string end after — match
Why \b\d\d\b matches '40' and '63' but rejects '7' and '512'.

The subject:512 case is the interesting one. The engine can match pattern:\d\d against subject:51, but the closing pattern:\b then checks the gap between subject:1 and subject:2. Two digits touching means two word characters, so no boundary — the whole attempt is thrown out.

In the second string, subject:81,92,03, every pair is fenced off by commas, so all three match.

The catch: \b is Latin-only

There’s a real limitation you need to keep in mind. The whole definition of pattern:\b rests on pattern:\w, and pattern:\w only recognizes Latin letters a-z, digits, and underscore. Any character outside that set counts as “non-word,” including letters from most of the world’s writing systems.

Because those letters read as non-word to the engine, a boundary appears at the wrong places — for instance right in the middle of a Cyrillic word, wherever it borders a Latin character, and nowhere along a run of purely non-Latin letters. If you need real Unicode-aware word matching, reach for the pattern:u flag together with Unicode property escapes such as pattern:\p{L} (any letter), and build your own boundary logic from lookarounds rather than trusting pattern:\b.

café·
In “café”, \b wrongly fires between f and é, because é is not \w. No boundary sits after é at all.
\b treats accented and non-Latin letters as non-word, so boundaries land in surprising spots.

Quick reference

AnchorMatches the position…
^at the start of the string (or line, with the m flag)
$at the end of the string (or line, with the m flag)
\bbetween a \w and a non-\w (a word edge)
\Beverywhere \b does not — the negation
The four anchors that match a position rather than a character.

The uppercase pattern:\B is the exact inverse: it asserts you are not at a word boundary. It’s handy for finding a pattern only when it’s embedded inside a larger word — for example pattern:\Band\B matches the subject:and buried inside subject:sandy but not the standalone word subject:and.

Switch between the three patterns below over the same sentence and watch which occurrences of subject:and light up:

interactive\b vs \B on the same string

None of these anchors consume characters. They slot between the characters your other subpatterns match, quietly checking that you’re standing in the right place before the engine moves on.