Multi-state styling
Every match gets a base style. Specific occurrences get additional layered styles on top.
How it works
Pass a states array. Each entry has a name, exactly one selector field (index, range, or indices), and a className and/or style:
import { Highlight } from 'one-more-highlight';
<Highlight
text={text}
searchWords={['time']}
highlightClassName="hl-base"
states={[
{ name: 'preview', range: [0, 1], className: 'hl-preview' },
{ name: 'active', index: 2, className: 'hl-active' },
{ name: 'bookmarked', indices: [3, 5], className: 'hl-bookmark' },
]}
/>
Every match gets hl-base. Match #2 also gets hl-active. Matches #0 and #1 also get hl-preview. Matches #3 and #5 also get hl-bookmark.
Selectors
HighlightState is a discriminated union — each entry carries exactly one selector field:
| Selector field | Selects |
|---|---|
index: i | A single match by zero-based index |
range: [a, b] | Matches from index a to b (inclusive) |
indices: [i, j, k] | A specific list of match indices |
Indexing is global document order — match #0 is the first occurrence in the text regardless of which searchWords entry produced it.
Stable references when search terms change
If your app's searchWords is dynamic (e.g., an autocomplete that adds
new queries as the user types), state selectors tied to global match
index will silently retarget every time a new query is added. Use
per-term selectors instead:
function SearchResults({ queries, text }: { queries: string[]; text: string }) {
return (
<Highlight
text={text}
searchWords={queries}
states={queries.map((q, i) => ({
name: q,
term: i,
className: `query-${i}`,
}))}
/>
);
}
Each query keeps its color as the user adds or removes queries.
Multi-state composition
A match can belong to multiple states simultaneously. Their classNames are concatenated (via clsx) and their style objects are shallow-merged in declaration order:
states={[
{ name: 'a', index: 0, className: 'text-bold', style: { color: '#1b1b1d' } },
{ name: 'b', index: 0, style: { background: '#FFF166' } },
]}
// Match 0 gets: className="hl-base text-bold", style={{ color: '#1b1b1d', background: '#FFF166' }}