A snail and a hare in a race

How fast are web components?

Joeri Sebrechts

It is often said that web components are slow. This was also my experience when I first tried building web components a few years ago. At the time I was using the Stencil framework, because I didn't feel confident that they could be built well without a framework. Performance when putting hundreds of them on a page was so bad that I ended up going back to React.

But as I've gotten deeper into vanilla web development I started to realize that maybe I was just using it wrong. Perhaps web components can be fast, if built in a light-weight way. This article is an attempt to settle the question "How fast are web components?".

The lay of the land

What kinds of questions did I want answered?

To figure out the answer I made a benchmark as a vanilla web page (of course), that renders thousands of very simple components containing only <span>.</span> and measured the elapsed time. This benchmark was then run on multiple devices and multiple browsers to figure out performance characteristics. The ultimate goal of this test is to figure out the absolute best performance that can be extracted from the most minimal web component.

To get a performance range I used two devices for testing:

Between these devices is a 7x CPU performance gap.

The test

The test is simple: render thousands of components using a specific technique, call requestAnimationFrame() repeatedly until they actually render, then measure elapsed time. This produces a components per millisecond number.

The techniques being compared:

This test was run on M1 in Brave, Chrome, Edge, Firefox and Safari. And on Chi in Chrome and Firefox. It was run for 10 iterations and a geometric mean was taken of the results.

The results

First, let's compare techniques. The number here is components per millisecond, so higher is better.

Author's note: the numbers from the previous version of this article are crossed out.

Chrome on M1
techniquecomponents/ms
innerHTML143 135
append233 239
append (buffered)228 239
shadow + innerHTML132 127
shadow + append183 203
template + append181 198
textcontent345
direct461
lit133 137
react pure275 338
react + wc172 212
append (norender)1393
shadow (norender)814
direct (norender)4277
lit (norender)880
Chrome on Chi, best of three
techniquecomponents/ms
innerHTML25 29
append55 55
append (buffered)56 59
shadow + innerHTML24 26
shadow + append36 47
template + append45 46
textcontent81
direct116
lit30 33
react pure77 87
react + wc45 52
append (norender)434
shadow (norender)231
direct (norender)1290
lit (norender)239

One relief right off the bat is that even the slowest implementation on the slow device renders 100.000 components in 4 seconds. React is roughly in the same performance class as well-written web components. That means for a typical web app performance is not a reason to avoid web components.

As far as web component technique goes, the performance delta between the fastest and the slowest technique is around 2x, so again for a typical web app that difference will not matter. Things that slow down web components are shadow DOM and innerHTML. Appending directly created elements or cloned templates and avoiding shadow DOM is the right strategy for a well-performing web component that needs to end up on the page thousands of times.

On the slow device the Lit framework is a weak performer, probably due to its use of shadow DOM and JS-heavy approaches. Meanwhile, pure React is the best performer, because while it does more work in creating the virtual DOM and diffing it to the real DOM, it benefits from not having to initialize the web component class instances. Consequently, when wrapping web components inside React components we see React's performance advantage disappear, and that it adds a performance tax. In the grand scheme of things however, the differences between React and optimized web components remains small.

The fast device is up to 5x faster than the slow device in Chrome, depending on the technique used, so it is really worth testing applications on slow devices to get an idea of the range of performance.

Next, let's compare browsers:

M1, append, best of three
browsercomponents/ms
Brave146 145
Chrome233 239
Edge224 237
Firefox232 299
Safari260 239
Chi, append, best of three
browsercomponents/ms
Chrome55 55
Firefox180 77

Brave is really slow, probably because of its built-in ad blocking. Ad blocking extensions also slow down the other browsers by a lot. Safari, Chrome and Edge end up in roughly the same performance bucket. Firefox is the best performer overall. Using the "wrong" browser can halve the performance of a machine.

Author's note: due to a measurement error in measuring elapsed time, the previous version of this article had Safari as fastest and Firefox as middle of the pack.

There is a large performance gap when you compare the slowest technique on the slowest browser on the slowest device, with its fastest opposite combo. Specifically, there is a 16x performance gap:

That means it becomes worthwhile to carefully consider technique when having to support a wide range of browsers and devices, because a bad combination may lead to a meaningfully degraded user experience. And of course, you should always test your web app on a slow device to make sure it still works ok.

Bottom line

I feel confident now that web components can be fast enough for almost all use cases where someone might consider React instead.

However, it does matter how they are built. Shadow DOM should not be used for smaller often used web components, and the contents of those smaller components should be built using append operations instead of innerHTML. The use of web component frameworks might impact their performance significantly, and given how easy it is to write vanilla web components I personally don't see the point behind Lit or Stencil. YMMV.

The full benchmark code and results can be found on Github.