JavaScript RegExp d Modifier

Beginner
⏱️ 9 min read
📚 Updated: Jul 2026
🎯 5 Examples
🚀 5 Try-it labs
Indices flag

What You’ll Learn

The d modifier (also called the indices or hasIndices flag) adds an indices property to match results. Each entry tells you exactly where the full match and every capture group start and end in the original string—ideal for editors, highlighters, and precise replacements.

01

d flag

Indices

02

hasIndices

RegExp prop

03

[start,end)

Position pairs

04

Groups

indices[1]…

05

Named

indices.groups

06

Not \d

Flag vs digit

Introduction

By default, a successful exec() or match() call gives you the matched text and capture groups, plus an index for the overall match. When you also need the exact start and end position of each capture group, add the d modifier.

The result is an indices array: parallel to the match array, with [start, end) ranges for the full match and every group. This is especially useful when building syntax highlighters, search UIs, or tools that wrap matched text in markup without re-searching the string.

Understanding the d Modifier

Append d to a regex literal or pass "d" in the constructor flags string. The RegExp instance exposes hasIndices: true, and match results include an indices property.

JavaScript
const re = /(\w+)@(\w+)/d;
const m = re.exec("Contact alice@example.com today");

m[0];            // "alice@example.com"
m[1];            // "alice"
m[2];            // "example.com"

m.indices[0];    // [8, 25]  full match range
m.indices[1];    // [8, 13]  group 1
m.indices[2];    // [14, 25] group 2

Ranges use half-open intervals: start is inclusive, end is exclusive—the same convention as String.slice(start, end).

💡
Beginner Tip

Do not confuse the d modifier (indices flag) with the \d metacharacter (match a digit). The modifier is a trailing flag on the regex; \d appears inside the pattern.

How to Use the d Modifier

Enable indices on literals, constructors, and combined flag strings:

JavaScript
/hello/d;                        // literal with d
new RegExp("hello", "d");        // constructor
/cat/gd;                         // global + indices

re.hasIndices;                   // true when d is set
m.indices.groups?.year;          // named group range

Use exec() for one match, or matchAll() with gd to collect indices for every occurrence in a string.

📝 Syntax

JavaScript
/pattern/d              // indices on one match
/pattern/gd             // indices on every match (with g)
new RegExp("p", "d")    // constructor form
re.hasIndices           // true | false
result.indices[n]       // [start, end) for match index n
result.indices.groups   // named capture ranges

Common patterns

  • /pattern/d — single match with index ranges.
  • /pattern/gd — all matches with indices via matchAll().
  • /(?<name>...)/d — named groups in indices.groups.
  • re.hasIndices — check whether the flag is active.

⚡ Quick Reference

GoalPattern / API
Enable indices/pattern/d
Check flagregex.hasIndices
Full match rangematch.indices[0]
Capture group 1 rangematch.indices[1]
Named group rangematch.indices.groups.name
All matches + indicestext.matchAll(/p/gd)

📋 d vs index vs \d vs g

Four related but distinct regex concepts beginners often mix up.

d modifier
/pat/d

Group index ranges

index property
match.index

Start of full match only

\d metachar
\d

Match a digit

g modifier
/pat/g

Find all matches

Examples Gallery

Open DevTools Console (F12) or use Try-it links. Examples cover exec indices, hasIndices, highlighting, named groups, and matchAll with gd.

📚 Getting Started

Read index ranges from a single exec() call.

Example 1 — Capture Group Indices with exec()

Parse an email and read where each part sits in the string.

JavaScript
const re = /(\w+)@(\w+)/d;
const m = re.exec("Contact alice@example.com today");

console.log(m[0]);           // "alice@example.com"
console.log(m.indices[0]);   // [8, 25]
console.log(m.indices[1]);   // [8, 13]  → "alice"
console.log(m.indices[2]);   // [14, 25] → "example.com"
Try It Yourself

How It Works

indices[n] aligns with m[n]. Slice the original string with text.slice(start, end) to recover each captured substring without storing it separately.

📈 Practical Patterns

Check the flag, highlight text, and iterate all matches.

Example 2 — Read the hasIndices Property

Confirm whether a RegExp was created with the d flag.

JavaScript
const withD = /cat/d;
const withoutD = /cat/;

console.log(withD.hasIndices);     // true
console.log(withoutD.hasIndices);  // false
console.log(withD.flags);          // "d"
Try It Yourself

How It Works

hasIndices mirrors the d flag at creation time, similar to how global reflects g. Flags are fixed once the RegExp object is built.

Example 3 — Highlight a Match Using Indices

Wrap the matched substring in brackets without searching the string again.

JavaScript
function highlight(text, re) {
  const m = re.exec(text);
  if (!m?.indices) return text;
  const [start, end] = m.indices[0];
  return text.slice(0, start) + "[" + text.slice(start, end) + "]" + text.slice(end);
}

console.log(highlight("Find the word hello here", /hello/d));
// "Find the word [hello] here"
Try It Yourself

How It Works

indices[0] gives the full-match span. Use it to insert markup or apply editor decorations at exact character offsets.

Example 4 — Named Group Indices

Read position ranges for named capture groups via indices.groups.

JavaScript
const re = /(?<year>\d{4})-(?<month>\d{2})/d;
const m = re.exec("Event on 2026-07-17");

console.log(m.groups.year);              // "2026"
console.log(m.indices.groups.year);      // [9, 13]
console.log(m.groups.month);             // "07"
console.log(m.indices.groups.month);     // [14, 16]
Try It Yourself

How It Works

Named groups appear in both groups (values) and indices.groups (ranges). Handy when different parts of a match need different styling in a UI.

Example 5 — All Matches with matchAll() and gd

Collect index ranges for every occurrence in a string.

JavaScript
const text = "cat dog cat";
const re = /cat/gd;

for (const m of text.matchAll(re)) {
  console.log(m[0], "at", m.indices[0]);
}
// "cat" at [0, 3]
// "cat" at [8, 11]
Try It Yourself

How It Works

Combine g (find all) with d (include indices). Each iterator value is a full match object with its own indices array.

🚀 Common Use Cases

  • Syntax highlighting — color each capture group at its exact offset in source code.
  • Search UIs — scroll to and underline matches using start/end positions.
  • Structured parsing — extract substrings by range without redundant indexOf calls.
  • Editor plugins — decorate named groups differently in IDE extensions.
  • Validation feedback — point error squiggles at the precise invalid segment.
  • Debugging regex — verify groups land where you expect in complex patterns.

Important Considerations

  • Not \d — the d flag is a modifier; \d matches digits inside the pattern.
  • ES2022 feature — older browsers may not provide indices; check support before relying on it.
  • Undefined groups — optional groups that skip produce undefined in indices, not [0, 0].
  • Half-open rangesend is exclusive; use slice(start, end) consistently.
  • Combine with g — for multiple matches, use gd with matchAll() or an exec() loop.

🧠 How the d Modifier Adds Indices

1

Match as usual

The engine finds the pattern and fills the match array and groups.

Match
2

Record offsets

Because d is set, the engine stores start/end offsets for the full match and each group.

Track
3

Build indices

Results expose indices parallel to captures, plus indices.groups for names.

Expose
4

Use ranges

Slice, highlight, or decorate the original string at exact character positions.

📝 Notes

  • The d flag is also known as hasIndices in the specification.
  • match.index only covers the full match; indices covers every group.
  • Previous topic: \ooo octal escape metacharacter.
  • Next modifier: g global flag.
  • See \d for the digit metacharacter (unrelated to this flag).
  • Flag order in literals is flexible: /cat/dg and /cat/gd are equivalent.

Browser & Runtime Support

The d (indices / hasIndices) modifier is an ES2022 feature with solid support in current browsers and Node.js.

Baseline · ES2022

RegExp d flag

Supported in modern Chrome, Firefox, Safari, and Node.js 16+. Older environments may omit match.indices.

92% Modern runtimes
Google Chrome Supported · Desktop & Mobile
Full support
Mozilla Firefox Supported · Desktop & Mobile
Full support
Apple Safari Supported · macOS & iOS
Full support
Microsoft Edge Supported · Chromium
Full support
Internet Explorer No native support · Use a polyfill
Polyfill
Opera Supported · Modern versions
Full support
Samsung Internet Supported · Android
Full support
Bun Supported · JavaScript runtime
Supported
Deno Supported · JavaScript runtime
Supported
Node.js Supported · Server runtime
Supported
Android WebView Supported · Modern WebView
Full support
d Very good

Bottom line: Safe for modern apps. Feature-detect with regex.hasIndices or check for match.indices before use in legacy targets.

Conclusion

The d modifier adds an indices property to match results, giving you precise [start, end) ranges for the full match and every capture group. It is the right tool when you need positions—not just matched text.

Remember: d is a flag, not the \d digit metacharacter. Pair it with g for matchAll(), and read named ranges from indices.groups. Next, learn the g global modifier.

💡 Best Practices

✅ Do

  • Use d when you need per-group start/end positions
  • Combine gd with matchAll() for all matches
  • Slice with text.slice(start, end) using index ranges
  • Check hasIndices before assuming indices exists
  • Use indices.groups for named capture positions

❌ Don’t

  • Confuse the d modifier with \d digit matching
  • Assume indices exists without the d flag
  • Treat end as inclusive (it is exclusive)
  • Re-search the string when indices already give exact ranges
  • Expect full support in very old browsers without a fallback

Key Takeaways

Knowledge Unlocked

Five things to remember about the RegExp d modifier

Get exact match and capture group positions in one exec call.

5
Core concepts
📈 02

indices[n]

[start,end).

Ranges
🚫 03

Not \d

Different.

Compare
🔒 04

.groups

Named ranges.

Named
🔢 05

gd

matchAll.

Combine

❓ Frequently Asked Questions

The d flag (indices / hasIndices) adds an indices property to match results. Each entry is a [start, end) pair showing where the full match and each capture group appeared in the original string.
No. The d flag is a regex modifier that records match positions. The \d metacharacter matches a digit character. They look similar on paper but serve completely different roles.
Add d to the flags: /pattern/d, /pattern/gd, or new RegExp('pattern', 'd'). The hasIndices property on the RegExp instance becomes true.
indices is an array parallel to the match array. indices[0] is the full match range, indices[1] is capture group 1, and so on. Each value is [start, end) or undefined when a group did not participate.
Not always. A single exec() or match() call works with d alone. Combine gd when you want indices for every match via matchAll() or repeated exec() loops.
The indices flag is part of ES2022. It works in modern Chrome, Firefox, Safari, and Node.js 16+. Older runtimes ignore the flag or lack indices on results.
Did you know?

Before the d flag, getting capture-group positions required re-running indexOf on each group value or walking the string manually. The indices feature lets the engine report all ranges in a single match call—faster and less error-prone for complex patterns.

Continue to g global modifier

Learn how the g flag finds every match in a string, not just the first one.

g modifier tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

6 people found this page helpful