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
match: (ctx) => booleanEvery match the predicate accepts — see predicate selectors

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' }}