Skip to content

Which API Should I Use

Jace edited this page Jun 6, 2026 · 1 revision

Which API Should I Use?

@highlighters exposes two layers of choice. First, which entry point marks the text: highlight, highlightAll, highlightSelection, or group. Second, in a framework, which surface drives that entry point: a component, or a hook / composable / action. This guide helps you pick both.

If you have not installed the library yet, start with Getting-Started.


The four render entry points

All four live in @highlighters/core and return a handle you keep around to show, hide, update, and remove. See API-Reference for full signatures and Options-Reference for every option.

highlight(target, options?, host?) — one explicit thing

The primary entry point. You hand it a concrete target and it marks it once. The target can be an element, a CSS selector string, a Range, a Selection, a text query ({ text, root? }), or a page target ({ root?, include?, exclude? }).

import { highlight } from "@highlighters/core";

// An element
highlight(document.querySelector("#intro"));

// A CSS selector
highlight(".lede");

// A text query: every match within root
highlight({ text: "deterministic", root: document.body });

The default snap is derived from the target shape: elements and selectors snap to "line", while Range, Selection, and text queries snap to "word". See Snapping-and-Overshoot.

Good for: marking specific, known content — a heading, a quoted phrase, a search hit. Anything where you decide what gets marked and when.

Returns an inert no-op handle when there is no DOM (SSR) or when the target resolves to zero ranges, so calling it is always safe. See SSR-Support.

highlightAll(options?) — declarative, whole-page, self-syncing

Collects every [data-highlight] element plus a page scan rooted at document.body (honouring exclusions), marks them all under one handle, then attaches a debounced MutationObserver so nodes added later get marked and removed nodes drop their marks without a full rescan. The default snap is "line".

import { highlightAll } from "@highlighters/core";

const handle = highlightAll({ palette: "fluorescent" });
<p data-highlight>This paragraph is marked declaratively.</p>

The attribute is selected purely by presence ([data-highlight]); there are no documented attribute values. See Page-Highlighting.

Good for: content-driven sites where authors tag what to highlight in markup, or live regions where the DOM changes after load. One call covers current and future matches.

Trade-off: it owns a watcher for the lifetime of the handle. remove() disconnects it.

highlightSelection(options?) — the live user selection

Tracks the user's selection in real time via selectionchange and pours ink as they drag. The default snap is "word".

import { highlightSelection } from "@highlighters/core";

const handle = highlightSelection({ markType: "highlight" });
// later: handle.remove() detaches all listeners

Behaviour worth knowing:

  • On coarse pointers (touch) it returns an inert handle and defers to the native selection UI — no overlay.
  • A backward drag (focus before anchor) pours ink from the right edge.
  • When the selection empties it fades out over 200ms then drops the bands, gated by fadeOnClear (instant clear under reduced motion). Re-selecting cancels the fade.
  • The speed option group (Beta) engages only here, and only during a primary-button fine-pointer (mouse / pen) drag. It is a byte-identical no-op for programmatic, keyboard, or static selections, under prefers-reduced-motion, or when speed.enabled is false.

See Selection-Highlighting.

Good for: reader tools, annotation UIs, "highlight as you read" experiences.

group(handles) — choreograph several marks

Bundles existing handles so they reveal in sequence. show() reveals members in array order, so their draw-on staggers like a pen travelling down the page.

import { highlight, group } from "@highlighters/core";

const marks = group([
  highlight("#first"),
  highlight("#second"),
  highlight("#third"),
]);

marks.show(); // staggered draw-on, in array order

A GroupHandle is not a render entry point on its own — it wraps marks you already created with highlight (or the other entry points). See Animation for the draw-on and stagger details.

Good for: sequenced reveals, scrollytelling, walkthroughs where marks should appear one after another.


Entry-point decision table

You want to... Use Default snap Auto-sync DOM?
Mark one element, selector, range, or text query you name highlight derived from target No
Mark everything tagged [data-highlight] plus a page scan, kept in sync highlightAll "line" Yes (MutationObserver)
Mark the user's live selection as they drag highlightSelection "word" n/a (tracks selectionchange)
Reveal several existing marks in sequence group inherited per member No

All four return a handle whose remove() restores the pre-highlight DOM and disconnects any observers or listeners. highlight and highlightAll return inert no-op handles outside a DOM.


Framework surfaces: component vs hook / composable / action

The bindings (@highlighters/react, @highlighters/vue, @highlighters/svelte) wrap highlight — they apply a mark to an element you render. For highlightAll, highlightSelection, and group, call the core functions directly from an effect or lifecycle hook; there is no dedicated component for them.

Accuracy note: some binding JSDoc examples show options={{ preset: "wet", color: "pink" }}. There is no preset option and no named-color shorthand. color is a CSS color string or a { palette, swatch } object, and palettes are selected with palette / color. Treat those snippets as illustrative only. See Color-and-Palettes and Options-Reference.

React: Highlight component vs useHighlight hook

The component is the simplest path. It renders an element (default <span>, polymorphic via as) whose text content is highlighted, and passes extra HTML attributes through to that element.

import { Highlight } from "@highlighters/react";

<Highlight as="p" options={{ palette: "mild", markType: "underline" }}>
  This sentence is marked.
</Highlight>

The hook gives you the handle and lets you own the element. Pass a ref, a DOM node, or a core Target; it returns a ref to the live MarkHandle. It re-checks each render, so a RefObject that populates after mount is picked up, and option changes are pushed via handle.update() without re-seeding geometry.

import { useRef } from "react";
import { useHighlight } from "@highlighters/react";

function Lede() {
  const ref = useRef<HTMLParagraphElement>(null);
  const mark = useHighlight(ref, { opacity: 0.6 });
  // mark.current is the MarkHandle (or null before mount)
  return <p ref={ref}>This sentence is marked.</p>;
}

For highlightAll / highlightSelection, call the core function inside useEffect and keep the handle:

import { useEffect } from "react";
import { highlightSelection } from "@highlighters/core";

useEffect(() => {
  const handle = highlightSelection({ speed: { enabled: true } });
  return () => handle.remove();
}, []);

Use the component for static, declarative marks on content you render. Use the hook when you need the MarkHandle (to show / hide / group), when the target is a Range / text query, or when the element comes from elsewhere.

Vue: Highlight component vs useHighlight composable

The component renders its default slot inside an element (default <span>, via the as prop) and highlights its text. It exposes { el, handle }, where handle is a getter for the live MarkHandle.

<script setup>
import { Highlight } from "@highlighters/vue";
</script>

<template>
  <Highlight as="p" :options="{ palette: 'vintage' }">
    This sentence is marked.
  </Highlight>
</template>

The composable binds a template ref (or a core Target) and returns a getter for the MarkHandle. Options can be a ref; changes are pushed via handle.update() through a deep watch.

<script setup>
import { ref } from "vue";
import { useHighlight } from "@highlighters/vue";

const el = ref(null);
const getMark = useHighlight(el, { markType: "strike-through" });
</script>

<template>
  <p ref="el">This sentence is marked.</p>
</template>

For highlightAll / highlightSelection, call the core function in onMounted and clean up in onBeforeUnmount.

Use the component for declarative slot content. Use the composable when you need the handle, a reactive options ref, or full control of the DOM structure.

Svelte: the highlight action

Svelte provides only an action — the idiomatic way to attach DOM behaviour to an element, with no wrapper and no extra component. It highlights the element's text content; its update pushes new options through the live handle (preserving geometry) and destroy calls remove().

<script>
  import { highlight } from "@highlighters/svelte";
  let options = { palette: "calm" };
</script>

<p use:highlight={options}>This sentence is marked.</p>

Reassigning the parameter object triggers the action's update automatically:

<script>
  import { highlight } from "@highlighters/svelte";
  let opacity = 0.55;
  $: options = { opacity };
</script>

<input type="range" bind:value={opacity} min="0" max="1" step="0.01" />
<p use:highlight={options}>This sentence is marked.</p>

For highlightAll / highlightSelection, call the core function in onMount and return its remove from the cleanup function.


Framework comparison table

React component React hook Vue component Vue composable Svelte action
Wraps highlight highlight highlight highlight highlight
Renders an element Yes (as, default span) No — you do Yes (as, default span) No — you do No — you do
Gives you the MarkHandle No Yes (ref) Via handle getter (component expose) Yes (getter) Via action lifecycle
Option updates Re-render → update() update(), no re-seed Reactive prop → update() Deep watch → update() Action update
Custom tag as prop You control it as prop You control it You control it
Other entry points call core in useEffect call core in useEffect call core in lifecycle call core in lifecycle call core in onMount

When to use which

  • Use the component when you render the content and want the simplest declarative mark.
  • Use the hook / composable / action when you need the MarkHandle (for show / hide / group choreography), when the target is a Range, Selection, or text query, or when you cannot tolerate the component rendering the element for you.
  • For highlightAll, highlightSelection, and group, call the core functions directly from a lifecycle hook in any framework — there is no dedicated component, and they manage their own observers and listeners.

See also

Clone this wiki locally