Overlap strategies
When multiple searchWords entries match overlapping ranges, the overlapStrategy prop controls how they are resolved.
merge (default)
Overlapping matches are collapsed into a single segment spanning all of them. The resulting segment belongs to all matching states.
<Highlight
text="abcdef"
searchWords={['abc', 'cde']}
overlapStrategy="merge"
highlightClassName="hl"
/>
// → <mark>abcde</mark>f
Use merge when you want clean, non-overlapping rendered output and don't need to distinguish individual match contributions.
nest
Each match is kept as an individually addressable segment. Overlapping matches are not split — both marks are emitted in their entirety and may overlap in the DOM.
<Highlight
text="abcdef"
searchWords={['abc', 'cde']}
overlapStrategy="nest"
highlightClassName="hl"
/>
// → <mark>abc</mark><mark>cde</mark>f
// Both marks exist; "abc" and "cde" overlap at "c"
Use nest with states when you need every match individually addressable by index (e.g. for keyboard navigation or per-match styling) and can tolerate overlapping DOM elements.
first-wins
Later matches that overlap an earlier match are dropped entirely. The order of searchWords becomes a priority ranking: earlier terms claim overlapping ranges, later terms are discarded where they collide.
<Highlight
text="abcdef"
searchWords={['abc', 'cde']}
overlapStrategy="first-wins"
highlightClassName="hl"
/>
// → <mark>abc</mark>def ("cde" dropped because it overlaps "abc")
Why this matters
Most existing solutions only expose merge-style overlap resolution, with no way to express that some terms are more important than others. first-wins turns the searchWords array itself into a priority list — no extra prop, no custom matcher needed.
Use cases
Exact phrase preferred over loose matches. Put the multi-word phrase first; single-word fallbacks only highlight where the phrase didn't already win.
<Highlight
text="machine learning models use machine learning techniques"
searchWords={['machine learning', 'machine', 'learning']}
overlapStrategy="first-wins"
highlightClassName="hl"
/>
// → both "machine learning" phrases highlight as one span;
// no separate highlights for the words inside them
Severity or warning terms preferred over a general search. Stack [...criticalTerms, ...userQuery] so warnings claim overlapping spans whenever a critical term covers the same range as a user query term — leaving only the critical highlight visible.
<Highlight
text="error: invalid input"
searchWords={[/\b(error|warning|invalid)\b/i, /input/i]}
overlapStrategy="first-wins"
highlightClassName="hl-critical"
/>
// "invalid" and "input" don't overlap, so both highlight;
// if they ever shared a range, "invalid" would win.
Currently-selected entity preferred over its siblings. When highlighting all entity names from a list, put the active one first so it wins ties.
<Highlight
text="React, React Router, React Native"
searchWords={[currentlySelected.name, ...allEntities.map((e) => e.name)]}
overlapStrategy="first-wins"
highlightClassName="hl"
/>
When to choose first-wins over merge / nest
| Intent | Strategy |
|---|---|
| "Highlight every match; collapse overlaps into one span." | merge |
| "Highlight every match; keep them individually addressable even if they overlap in the DOM." | nest |
| "Earlier terms in my array are more important — discard later ones where they collide." | first-wins |