Using the Browser Highlight API
Published 2025-09-01, About 4 minute read.
The Hightligh API just recently became broadly available!
(Well, at the time of writing this I know it's
generally
available, so hopefully
the above widget information updates!) This API unlocks the ability to
highlight portions of text
without having to inject or modify DOM.
In this post I'll describe the problems we had with marking DOM text before,
the problems Highlight API solves, and how to possibly encapsulate this API in
a web component.
So what is the problem with using <mark>? 📎
Turns out, we already have a semantic element for highlighting or marking
certain regions of text:
the mark element. It's a handy element that has a nice, out-of-the-box styling.
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any living
situation.
Furthermore, their ability to maintain themselves and hunt makes them
low-maintenance and practical companions. Unlike dogs, who sometimes want continual attention and care from
their owners, cats are satisfied to do their own thing and enjoy the
company of their own. They have an inherent feeling of independence that
distinguishes them from other tamed animals. Furthermore, their slim and
nimble bodies move with elegance and beauty that no other animal can
match. They are natural born athletes, and it is a thrill to watch them
leap, pounce, and play.
Some reddit user
There are some nice libraries out there to help with this task as well and
highlight content programatically. Mark.js is one with a
straightforward API: hand Mark.js the node and the range to highlight. Mark.js
inserts mark tags for you where your text matches the content.
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any living
situation. Furthermore, their ability to maintain themselves and hunt
makes them low-maintenance and practical companions. Unlike dogs, who
sometimes want continual attention and care from their owners, cats are
satisfied to do their own thing and enjoy the company of their own. They
have an inherent feeling of independence that distinguishes them from
other tamed animals. Furthermore, their slim and nimble bodies move with
elegance and beauty that no other animal can match. They are natural
born athletes, and it is a thrill to watch them leap, pounce, and play.
Some reddit user
This is perfect if you have a lot of text and you want to programatically find
just like the browser:
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any living
situation. Furthermore, their ability to maintain themselves and hunt
makes them low-maintenance and practical companions. Unlike dogs, who
sometimes want continual attention and care from their owners, cats are
satisfied to do their own thing and enjoy the company of their own. They
have an inherent feeling of independence that distinguishes them from
other tamed animals. Furthermore, their slim and nimble bodies move with
elegance and beauty that no other animal can match. They are natural
born athletes, and it is a thrill to watch them leap, pounce, and play.
Some reddit user
So what's the issue?
Mark.js searches through the portion of the DOM tree that you give it,
searching for text content matches and wrapping those matches in more markup.
That extra markup can mess up your styles and your intended DOM structure!
Check this example out:
This isn't a contrived example. The structure of these buttons aren't
expecting extra mark elements, and flex notices that
there's a new child node and properly arranges those new nodes. This sort of
thing happened at work when I tried to just use Mark.js on a larger content
area that had tabs and tab panels. The tab layout and the content in the
panels would jump around because new mark elements were being
added from someone (me), and you could argue that we shouldn't have to change
DOM internally to get this highlight effect.
What's even worse is there's no way to have the mark elements say
"ignore me, please!" If we set mark to have
display: contents, we lose the highlighting color.
The Highlight API 📎
The Highlight API lets us manually mark regions of text as "highlighted", and
highlight these special regions using a ::highlight selector.
There are no new elements being added to the DOM.
In the example below, whenever the search string is changed, we regex search
the content of the blockquote. From the matchAll results, we can
pull out the character indices of where the string matches occur. For each of
those ranges, we create a new Range, and feed those ranges to
Highlight
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any living
situation. Furthermore, their ability to maintain themselves and hunt
makes them low-maintenance and practical companions. Unlike dogs, who
sometimes want continual attention and care from their owners, cats are
satisfied to do their own thing and enjoy the company of their own. They
have an inherent feeling of independence that distinguishes them from
other tamed animals. Furthermore, their slim and nimble bodies move with
elegance and beauty that no other animal can match. They are natural
born athletes, and it is a thrill to watch them leap, pounce, and play.
Some reddit user
Works like a charm! We're not going to run into issues with injected markup
becuase there is none!
Let's make our lives a bit easier... 📎
Admittedly, that's a lot of manual work to highlight text. Perhaps we could
encapsulate some of this work by creating a
web component that can take in text and an array of ranges to
highlight, and do it for us?
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any
living situation. Furthermore, their ability to maintain themselves
and hunt makes them low-maintenance and practical companions. Unlike
dogs, who sometimes want continual attention and care from their
owners, cats are satisfied to do their own thing and enjoy the company
of their own. They have an inherent feeling of independence that
distinguishes them from other tamed animals. Furthermore, their slim
and nimble bodies move with elegance and beauty that no other animal
can match. They are natural born athletes, and it is a thrill to watch
them leap, pounce, and play.
Some reddit user
Yes, it's a bit of work to get going, but now we can simply do this in a lit
template, for example:
${content}
And the important thing is that these ranges are reactive!
Let's see this with a live search...
Cats are intriguing animals that make excellent pets. Their
independence, grace, and intelligence allow them to adapt to any
living situation. Furthermore, their ability to maintain themselves
and hunt makes them low-maintenance and practical companions. Unlike
dogs, who sometimes want continual attention and care from their
owners, cats are satisfied to do their own thing and enjoy the company
of their own. They have an inherent feeling of independence that
distinguishes them from other tamed animals. Furthermore, their slim
and nimble bodies move with elegance and beauty that no other animal
can match. They are natural born athletes, and it is a thrill to watch
them leap, pounce, and play.
Some reddit user
You may ask, "Why not pass a search term to the highlighter?" That's not a bad
idea, but we would be coupling the searching strategy with the highlighting.
We could take the example above and instead of doing a regex match, use
something like fuse.js to do a fuzzy
search. Fuse.js provides index ranges as well, so we could just grab those and
assign them to our highligter ranges property.
One thing to note here is that every instance of
high-lighter shares the same highlight instance and highlight
registry. You have to create a highlight instance and register it with
CSS.highlights.set. We use only one highlight instance so that if
you're customizing the highlighting style using
::highlight() psuedo-element, you would only have to target one
highlight name.
That's it! 📎
I hope the examples have spurred some creativity and made the Highlight API a
little more understandable. Let me know what you think! Find me on Bluesky or Mastodon. I also have an RSS feed here