A lightweight React component that wraps the browser-native CSS Custom Highlight API for performant text highlighting without DOM manipulation.
- Zero DOM churn – highlights render via browser paint layers
- Composable ranges – stack multiple highlight keys with independent styles
- Search integration – built-in hook for live query updates
- Design-token friendly – style via CSS custom properties
- Accessible – preserves document structure and screen-reader flow
- TypeScript ready – full type safety with comprehensive definitions
pnpm dlx shadcn@latest add https://raw.githubusercontent.com/junaidanjum/text-highlight/main/public/r/text-highlighter.jsonThis copies the component and hook into your project so you can customize them directly.
Copy these files into your project:
registry/text-highlight/text-highlighter/text-highlighter.tsx→components/ui/text-highlighter.tsxregistry/text-highlight/text-highlighter/use-text-highlight.ts→hooks/use-text-highlight.ts
/* globals.css */
::highlight(default) {
background-color: #fef08a;
color: inherit;
}
::highlight(primary) {
background-color: #93c5fd;
color: inherit;
}import { TextHighlighter } from "@/components/ui/text-highlighter"
import { useState } from "react"
function SearchableContent() {
const [searchTerm, setSearchTerm] = useState("")
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
<TextHighlighter searchTerm={searchTerm} highlightKey="default">
<p>Your content here will be highlighted as you search.</p>
</TextHighlighter>
</div>
)
}import { TextHighlighter } from "@/components/ui/text-highlighter"
import { useTextHighlight } from "@/hooks/use-text-highlight"
function AdvancedSearch() {
const {
searchTerm,
setSearchTerm,
caseSensitive,
setCaseSensitive,
matchCount,
onHighlight,
} = useTextHighlight()
return (
<div>
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<label>
<input
type="checkbox"
checked={caseSensitive}
onChange={(e) => setCaseSensitive(e.target.checked)}
/>
Case sensitive ({matchCount} matches)
</label>
<TextHighlighter
searchTerm={searchTerm}
caseSensitive={caseSensitive}
highlightKey="primary"
onHighlight={onHighlight}
>
<div>Your searchable content...</div>
</TextHighlighter>
</div>
)
}| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
— | Content to highlight |
searchTerm |
string |
"" |
Search query |
highlightKey |
string |
"default" |
CSS ::highlight() key |
caseSensitive |
boolean |
false |
Match exact casing |
wholeWordsOnly |
boolean |
false |
Match whole words only |
onHighlight |
(count: number) => void |
— | Match count callback |
Returns searchTerm, setSearchTerm, caseSensitive, setCaseSensitive, wholeWordsOnly, setWholeWordsOnly, highlightKey, setHighlightKey, matchCount, onHighlight, and clearSearch.
| Browser | Version |
|---|---|
| Chrome/Edge | 105+ |
| Safari | 17.2+ |
| Firefox | 130+ |
git clone https://github.com/junaidanjum/text-highlight.git
cd text-highlight
pnpm install
pnpm dev # start dev server
pnpm test:run # run tests
pnpm build # build demo siteMIT