CSS Custom Highlight API
Problem
Ever tried building a search and highlight feature? Like, when you type something in a search box, and it shows a list of results with the matching text highlighted?

The most challenging part of this feature is the highlighting itself. On every keydown, you need to highlight the matching text in the DOM. But how do you actually do that? You can only apply CSS styles using selectors like class, attribute, or tag. But with plain text, there's nothing to hook into—just the text itself. That means, while the user is typing, we need to modify the DOM by wrapping the matching text in HTML tags like:
<span class="to-be-highlighted">
And that's the most annoying part—we have to modify the DOM every time the user types.
Sometimes it can even lead to unexpected results.
For example, I was working on a project that used the Angular framework. We had this search and highlight feature there, but the text that needed to be highlighted was coming from Angular interpolation.
Like:
Then I found out when the textProvider
variable changed - the view didn’t update at all—as if nothing had happened.
After digging a bit, I realized that interpolation only stopped working after performing a search in the search bar. The reason was that the text inside the <span>
tags was being replaced using the innerHTML
property, which happens outside of Angular's context. Angular just loses the connection after that kind of DOM intervention.
Solution
There's a CSS Custom Highlight API that lets you highlight text ranges in the DOM. The best part? You can do it without modifying the DOM! Without this API, your only option is to wrap the text you want to highlight in an HTML tag like span
, using the innerHTML
property, as mentioned above. Try searching online—you’ll find tons of solutions that rely on that approach.
Now let’s take a closer look at the Highlight API.
How CSS Custom Highlight API works
Let’s see what this API is made of and how its parts work together.
Range object
First, you need a way to specify the text you want to highlight. You can do that using Range object(s) .
Highlight object
Then the Range(s) need to be added to a Highlight object . You can think of it like a Set object —or more accurately, a Set-like object .
Highlight Registry
The third step is to register the Highlight object in the HighlightRegistry , which is part of the CSS registry. Think of this registry as a bridge between the text you want to highlight and the styles you want to apply to it.
Take a look at the code—things should start to make more sense:
Define highlight styles
The last step is to define the styles you want to apply to your custom highlight selector (see the previous step). You can do this using the ::highlight pseudo-element.
Scheme
The only thing the developer needs to control is the Range
objects registered in a Highlight
object.
Why is that, you may ask? What’s the problem?
The key point is that a single page can have multiple search + highlight UIs running at the same time. But there's only one Highlight
object. So when a user clears a search bar, the app should remove only the Range
objects that were registered by that specific search UI—so it doesn't interfere with other search + highlight UIs on the page.
Take a look at the image below. It shows two search bars, both controlled by a single Highlight
object.
Since both search bars are controlled by a single Highlight
object, we need to track which one registers each Range
object. That way, when a user clears a search bar, the code removes only the Range
(s) associated with that specific search + highlight instance.
Downsides
This is great API which allows to prevent DOM modifications. But there are two things which can prevent you to use it:
- Browser support - in a time of writing Firefox still does not support it. They have a registered bug which was created years ago but still in development.
- Animation - it seems that there is no way to animate process of highlighting.
Github example
If you are using Angular framework then you can find an example here:
CSS Custom Highlight API through Angular Directive
It's not that hard at all. Try it.