npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

one-more-highlight

v1.7.0

Published

Multi-state substring highlighting for React and vanilla JS. Highlight every match in one style, plus specific occurrences (by index, range, or list) in another.

Readme

omh · one-more-highlight

Multi-state substring highlighting for React — and for no framework at all.

License: MIT npm version npm downloads CI Latest release types React

Dedicated to Chester Bennington. Inspired by the idea that every small light matters.

— "I tried so hard and got so far…" — we built this so the right words could shine.


Why this exists

one-more-highlight gives you:

  • TypeScript-first — full types and a discriminated-union HighlightState that narrows correctly on the selector field (index, range, indices, term, or a match predicate).
  • Multi-state styling as the headline feature — every match gets a base style, plus layered styles selected by index, range, or arbitrary list. Styles compose.
  • Bring your own matches — ranges={[…]} takes precomputed offsets from Algolia, a server-side matcher, an NLP tokenizer or AI citations instead of searchWords; the two are mutually exclusive at the type level.
  • Headless useHighlight hook alongside the <Highlight> component, with a renderMatch render-prop for full per-match control.
  • Tiny — ~2.7 KB brotlied (ESM), 2 microscopic deps (clsx + escape-string-regexp).
  • Modern — React 18+/19, ESM + CJS dual build with .d.ts + .d.cts, tree-shakeable, SSR-safe.
  • Works without React — one-more-highlight/vanilla runs the same pipeline over any DOM subtree, matching across element boundaries, with a <script src> build for pages that have no bundler.

Install

pnpm add one-more-highlight
# or: npm i one-more-highlight / yarn add one-more-highlight

Peer: react >= 18 (optional — not needed for the vanilla entry). Runtime deps: clsx, escape-string-regexp (both MIT, ~400 B combined).

Quick start

import { Highlight } from 'one-more-highlight';

<Highlight
  text="time time time time time"
  searchWords={['time']}
  highlightClassName="bg-yellow-200"
  states={[
    { name: 'active',     index: 2,         className: 'bg-orange-500 ring-2' },
    { name: 'preview',    range: [0, 1],    className: 'bg-blue-100' },
    { name: 'bookmarked', indices: [3, 4],  className: 'underline' },
  ]}
/>

A single match can be in multiple states at once; their classNames concatenate and their styles shallow-merge in declaration order.

Engines

one-more-highlight ships four rendering engines that share the same matching pipeline. The default <Highlight> from 'one-more-highlight' wraps each match in a DOM node; <CssHighlight> from 'one-more-highlight/css' paints via the CSS Custom Highlight API with zero per-match DOM nodes (faster on long text); <HighlightText> from 'one-more-highlight/native' renders nested <Text> runs for React Native; and one-more-highlight/vanilla needs no framework at all.

See engines/css-highlights, engines/react-native and engines/vanilla.

Vanilla JS — no framework, no build step

one-more-highlight/vanilla highlights text in any DOM subtree, including markup you don't own. It matches across element boundaries, paints with the CSS Custom Highlight API without touching the DOM, and falls back to <mark> injection where that API is missing.

Install it the same way — react is an optional peer dependency, so a plain-JS project pulls in nothing extra:

pnpm add one-more-highlight
# or: npm i one-more-highlight / yarn add one-more-highlight
import { Highlighter } from 'one-more-highlight/vanilla';

const h = new Highlighter(document.querySelector('#article'));
h.mark(['ipsum', 'dolor'], {
  states: [
    { name: 'term-ipsum', term: 'ipsum' },
    { name: 'term-dolor', term: 'dolor' },
  ],
});

Nothing renders until you style the highlight names — the package ships zero CSS by design:

::highlight(term-ipsum) { background: #FFF166; color: #1b1b1d; }
::highlight(term-dolor) { background: #FFADD6; color: #1b1b1d; }

Or skip the bundler entirely. The package ships an IIFE build (~3.2 KB brotlied) that defines a global, served straight from npm by jsDelivr and unpkg:

<script src="https://cdn.jsdelivr.net/npm/one-more-highlight@1/dist/omh.global.js"></script>
<script>
  new OMH.Highlighter(document.body).mark('time');
</script>

Everything the React entry can select — index, range, indices, term, nth, a match predicate — works identically here, plus markRanges() for precomputed offsets and the three overlap strategies.

📖 Full vanilla guide → — API reference, the two renderers, CDN pinning and SRI, cross-element matching, shadow DOM, and the document-global highlight-name registry (the one real footgun). Runnable examples live in examples/vanilla/.

Accessibility & navigation

one-more-highlight/a11y ships <AccessibleHighlight>, a drop-in replacement for <Highlight> with a mode prop ('native' | 'dual' | 'annotated') that makes the DOM-<mark>-vs-assistive-technology tradeoff explicit instead of accidental, plus <MatchAnnouncer>, a debounced role="status" live region for announcing result count and navigation position. one-more-highlight/navigation ships useRovingMatchFocus, a standard roving-tabindex hook for keyboard-navigating matches (arrow keys, Home/End) that composes with the core hook's getMatchNode.

import { AccessibleHighlight, MatchAnnouncer } from 'one-more-highlight/a11y';
import { useRovingMatchFocus } from 'one-more-highlight/navigation';

See api/accessible-highlight, api/match-announcer, and api/use-roving-match-focus.

Browser & runtime

React 18+/19 (optional), Node 18+, modern evergreens (Chrome 112+, Firefox 140+, Safari 16.4+). The vanilla entry's css renderer needs the CSS Custom Highlight API (Baseline since March 2026) and falls back to <mark> injection without it. Full matrix → recipes/browser-support.

Documentation

| Topic | Where | | --- | --- | | Getting started — install, intro, quick start | docs site → getting-started | | Guides — basic highlighting, headless hook, multi-state styling, render-prop | docs site → guides | | API reference — <Highlight> props, useHighlight, types, HighlightState selectors | docs site → api | | Recipes — accessibility, diacritic-insensitive search, overlap strategies, browser support | docs site → recipes | | Engines — CSS Custom Highlight API, React Native | docs site → engines | | Vanilla JS — no-framework engine, CDN usage, DOM walking | docs site → engines/vanilla | | Roadmap (v2+ plan) | docs/ROADMAP.md | | Architecture decisions | docs/adr/ |

Contributing

See CONTRIBUTING.md. Bug reports and edge-case fuzz cases especially welcome.

License

MIT © Ronen Mars. See LICENSE.


"In the end, it doesn't even matter" — except when it does. Every match. Every word. Every voice that mattered. R.I.P. Chester. 🤍