-
-
Notifications
You must be signed in to change notification settings - Fork 3
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.
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.
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.
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.
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 listenersBehaviour 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
200msthen drops the bands, gated byfadeOnClear(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 whenspeed.enabledisfalse.
Good for: reader tools, annotation UIs, "highlight as you read" experiences.
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 orderA 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.
| 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.
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 nopresetoption and no named-color shorthand.coloris a CSS color string or a{ palette, swatch }object, and palettes are selected withpalette/color. Treat those snippets as illustrative only. See Color-and-Palettes and Options-Reference.
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.
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 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.
| 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
|
- Use the component when you render the content and want the simplest declarative mark.
-
Use the hook / composable / action when you need the
MarkHandle(forshow/hide/ group choreography), when the target is aRange,Selection, or text query, or when you cannot tolerate the component rendering the element for you. -
For
highlightAll,highlightSelection, andgroup, 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.
- Getting-Started — installation and first mark
- API-Reference — full signatures for every entry point and handle
-
Options-Reference — every
HighlightOptionsfield and default -
Page-Highlighting —
highlightAlland[data-highlight] -
Selection-Highlighting —
highlightSelectionand speed-aware ink -
Animation — draw-on, stagger, and
groupchoreography -
Color-and-Palettes — palettes, swatches, and why there is no
preset -
SSR-Support — inert handles and the DOM-free
core/pathentry
Getting Started
The Mark
- Mark-Types-and-Shapes
- Tips-and-Edges
- Ink-and-Optics
- Color-and-Palettes
- Snapping-and-Overshoot
- Animation
Targeting
Reference
Production