Skip to content

Core API

markit(element, plugins?)

Creates a MarkIt instance bound to a root DOM element.

typescript
import { markit } from '@markitjs/core';

const instance = markit(document.getElementById('content'));

Parameters:

ParameterTypeDescription
elementHTMLElementRoot element to search within
pluginsMarkitPlugin[]Optional array of plugins

Returns: MarkitInstance

Instance Methods

mark(term, options?)

Highlight text matching the given term(s).

typescript
instance.mark('hello');
instance.mark(['hello', 'world']);
instance.mark('hello', { caseSensitive: true });

markRegExp(regexp, options?)

Highlight text matching a regular expression.

typescript
instance.markRegExp(/\d+/g);
instance.markRegExp(/hello/gi, { element: 'span', className: 'found' });

markRanges(ranges, options?)

Highlight specific character ranges in the text content.

typescript
instance.markRanges([
  { start: 0, length: 5 },
  { start: 10, length: 3 },
]);

unmark()

Remove all highlights from the container.

typescript
instance.unmark();

getMatches()

Get the current list of match results.

typescript
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.

typescript
instance.destroy();

Options

OptionTypeDefaultDescription
renderer'auto' | 'highlight-api' | 'dom' | 'overlay''auto'Rendering strategy
elementstring'mark'Wrapper element tag (DOM renderer only)
classNamestring'markit-match'CSS class on wrapper elements
highlightNamestring'markit-highlight'CSS Highlight API registry name
caseSensitivebooleanfalseCase-sensitive matching
ignoreDiacriticsbooleanfalseStrip diacritics before matching
acrossElementsbooleanfalseMatch across element boundaries
separateWordSearchbooleanfalseSplit term into individual words
accuracy'partially' | 'exactly' | 'startsWith' | 'complementary''partially'Match accuracy mode
synonymsSynonymMapSynonym expansion map
wildcards'disabled' | 'enabled' | 'withSpaces''disabled'Wildcard support
ignoreJoinersbooleanfalseIgnore zero-width characters
ignorePunctuationstring[]Punctuation characters to ignore
excludestring[]CSS selectors to exclude from search
iframesbooleanfalseEnable iframe traversal (same-origin only)
iframesTimeoutnumber5000Timeout in ms for iframe loading
debouncenumber0Debounce delay in ms for live search
batchSizenumber0Render matches in async batches of this size (0 = synchronous)
debugbooleanfalseLog 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

CallbackSignatureDescription
each(element, info) => voidCalled for each match
done(totalMatches) => voidCalled when highlighting completes
noMatch(term) => voidCalled when no matches are found
filter(textNode, term, matchIndex, totalMatches) => booleanReturn false to skip a match

Accuracy Modes

ModeBehaviorExample: searching "light"
'partially'Substring matchMatches "highlighter", "lightning"
'exactly'Whole word matchMatches "light" only, not "lighter"
'startsWith'Word-start matchMatches "light", "lighter", not "highlight"
'complementary'Whitespace-delimitedMatches "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:

typescript
instance.mark('the', {
  renderer: 'dom',
  batchSize: 500,
  done: (count) => {
    console.log(`Rendered ${count} matches`);
  },
});

How it works

  1. Search is always synchronousgetMatches() returns all results immediately, even while rendering is still in progress
  2. Rendering is split into batches — each batch of N matches is rendered in one frame, then yields to the browser via requestIdleCallback (or requestAnimationFrame as fallback)
  3. Callbacks fire after all batches completedone, each, and noMatch are called once rendering finishes

Behavior by value

batchSizeBehavior
0 (default)Synchronous — all matches rendered in one frame
> 0, matches ≤ batchSizeSynchronous — everything fits in a single batch
> 0, matches > batchSizeAsync — 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:

tsx
const ref = useHighlight(query, { batchSize: 500, renderer: 'dom' });

Angular directive:

html
<div [markitHighlight]="searchTerm" [markitOptions]="{ batchSize: 500 }"></div>

Angular service:

typescript
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.