Core API
markit(element, plugins?)
Creates a MarkIt instance bound to a root DOM element.
import { markit } from '@markitjs/core';
const instance = markit(document.getElementById('content'));Parameters:
| Parameter | Type | Description |
|---|---|---|
element | HTMLElement | Root element to search within |
plugins | MarkitPlugin[] | Optional array of plugins |
Returns: MarkitInstance
Instance Methods
mark(term, options?)
Highlight text matching the given term(s).
instance.mark('hello');
instance.mark(['hello', 'world']);
instance.mark('hello', { caseSensitive: true });markRegExp(regexp, options?)
Highlight text matching a regular expression.
instance.markRegExp(/\d+/g);
instance.markRegExp(/hello/gi, { element: 'span', className: 'found' });markRanges(ranges, options?)
Highlight specific character ranges in the text content.
instance.markRanges([
{ start: 0, length: 5 },
{ start: 10, length: 3 },
]);unmark()
Remove all highlights from the container.
instance.unmark();getMatches()
Get the current list of match results.
const matches = instance.getMatches();
// [{ start: 0, end: 5, text: 'hello', term: 'hello' }, ...]destroy()
Remove all highlights and free resources. Always call this when you're done.
instance.destroy();Options
| Option | Type | Default | Description |
|---|---|---|---|
renderer | 'auto' | 'highlight-api' | 'dom' | 'overlay' | 'auto' | Rendering strategy |
element | string | 'mark' | Wrapper element tag (DOM renderer only) |
className | string | 'markit-match' | CSS class on wrapper elements |
highlightName | string | 'markit-highlight' | CSS Highlight API registry name |
caseSensitive | boolean | false | Case-sensitive matching |
ignoreDiacritics | boolean | false | Strip diacritics before matching |
acrossElements | boolean | false | Match across element boundaries |
separateWordSearch | boolean | false | Split term into individual words |
accuracy | 'partially' | 'exactly' | 'startsWith' | 'complementary' | 'partially' | Match accuracy mode |
synonyms | SynonymMap | — | Synonym expansion map |
wildcards | 'disabled' | 'enabled' | 'withSpaces' | 'disabled' | Wildcard support |
ignoreJoiners | boolean | false | Ignore zero-width characters |
ignorePunctuation | string[] | — | Punctuation characters to ignore |
exclude | string[] | — | CSS selectors to exclude from search |
iframes | boolean | false | Enable iframe traversal (same-origin only) |
iframesTimeout | number | 5000 | Timeout in ms for iframe loading |
debounce | number | 0 | Debounce delay in ms for live search |
batchSize | number | 0 | Render matches in async batches of this size (0 = synchronous) |
debug | boolean | false | Log timing info to console |
How renderer: 'auto' works
When renderer is 'auto' (the default), MarkIt checks at runtime whether the CSS Custom Highlight API is available (CSS and CSS.highlights). If it is, the Highlight API renderer is used; otherwise the DOM wrapping renderer is used. The chosen renderer is reused for that instance until you pass a different renderer or destroy it. So in supported browsers you get zero-DOM-mutation highlighting; in unsupported environments (e.g. older browsers or Node/jsdom) you get DOM wrapping automatically.
Multiple instances
With the highlight-api or auto renderer, multiple MarkIt instances share one Highlight per highlightName. They do not overwrite each other when several highlighters are on the page. Use a custom highlightName to register a separate set of highlights (e.g. for different styles).
Browser support for Highlight API
The highlight-api (and auto) renderer uses the CSS Custom Highlight API. It is supported in Chrome 105+, Edge 105+, Safari 17.2+, and Firefox 140+. In older or unsupported environments, use renderer: 'dom' or rely on auto to fall back to DOM wrapping.
DOM renderer and framework bindings
The DOM renderer never removes the framework-owned text node. For matches in the middle or end of a node, it only updates that node's textContent to the "before" part and inserts the wrapper and "after" text. For matches at the start of a node, it inserts the wrapper before the node and keeps the node in place with only the "after" text. On unmark()/clear(), it merges wrapped text back into that same node. So Angular, React, and other frameworks keep updating the same DOM node and dynamic content (e.g. with markitContentKey or contentKey) stays correct.
Callbacks
| Callback | Signature | Description |
|---|---|---|
each | (element, info) => void | Called for each match |
done | (totalMatches) => void | Called when highlighting completes |
noMatch | (term) => void | Called when no matches are found |
filter | (textNode, term, matchIndex, totalMatches) => boolean | Return false to skip a match |
Accuracy Modes
| Mode | Behavior | Example: searching "light" |
|---|---|---|
'partially' | Substring match | Matches "highlighter", "lightning" |
'exactly' | Whole word match | Matches "light" only, not "lighter" |
'startsWith' | Word-start match | Matches "light", "lighter", not "highlight" |
'complementary' | Whitespace-delimited | Matches "light" surrounded by spaces/boundaries |
Batched Rendering
When highlighting a large number of matches (thousands+), rendering can freeze the UI. Set batchSize to a positive number to split rendering across animation frames:
instance.mark('the', {
renderer: 'dom',
batchSize: 500,
done: (count) => {
console.log(`Rendered ${count} matches`);
},
});How it works
- Search is always synchronous —
getMatches()returns all results immediately, even while rendering is still in progress - Rendering is split into batches — each batch of N matches is rendered in one frame, then yields to the browser via
requestIdleCallback(orrequestAnimationFrameas fallback) - Callbacks fire after all batches complete —
done,each, andnoMatchare called once rendering finishes
Behavior by value
batchSize | Behavior |
|---|---|
0 (default) | Synchronous — all matches rendered in one frame |
> 0, matches ≤ batchSize | Synchronous — everything fits in a single batch |
> 0, matches > batchSize | Async — split into ⌈matches/batchSize⌉ batches |
Cancellation
Calling mark(), unmark(), or destroy() while a batched render is in progress automatically cancels the pending batch. Only the most recent mark() call's results are rendered.
Framework compatibility
batchSize works with all framework wrappers — pass it through options like any other option:
React:
const ref = useHighlight(query, { batchSize: 500, renderer: 'dom' });Angular directive:
<div [markitHighlight]="searchTerm" [markitOptions]="{ batchSize: 500 }"></div>Angular service:
this.markitService.highlight(instance, term, { batchSize: 500 });When to use batching
Use batchSize when the DOM renderer produces 500+ matches and you want the UI to remain interactive during highlighting. The CSS Highlight API renderer is already non-blocking (zero DOM mutations), so batching provides less benefit there — but it still works.