Skip to main content

Multi-state styling

Every match gets a base style. Specific occurrences get additional layered styles on top.

[10:14:02] error: connection refused on port 5432 — retrying in 2s. [10:14:04] error: connection refused on port 5432 — retrying in 4s. [10:14:08] error: connection refused on port 5432 — retrying in 8s. [10:14:16] error: handshake failed after 3 retries. [10:14:16] warning: pool exhausted, no error logged this cycle. [10:14:30] error: timeout exceeded, giving up.
Open in StackBlitz

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 fieldSelects
index: iA 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' }}