In this article

Svelte really is that fast

The five most popular JS frameworks – Angular, React, Vue, Svelte and Blazor – use different rendering strategies, and it shows.

I haven’t Svelte this way about a JS framework in a very long time
The Svelte logo dashes across the canvas

I haven’t Svelte this way about a JS framework in a very long time

If you search online, you’ll find countless benchmarks claiming to compare the performance of various JavaScript frameworks with each other. More often than not, the benchmarks are overly simplistic and fail to reflect real-world scenarios. In other cases, they compare apples and oranges, for instance by pitting a fully fledged framework against a lightweight library that cover only a small subset of the framework’s functionality.

Right now, I’m at that point in my career again where I am starting development on a new web application and need to choose the “right” JavaScript framework. This time, I decided to look for academic studies on the performance of JavaScript frameworks and sadly, didn’t find as many as I had hoped. I did come across one particular study that compares Angular, React, Vue, Svelte and Blazor with each other. Its main drawback is that the comparison was done in 2021 – a lifetime ago in tech terms – but I think it’s still worth reading.

Before I dive into the summary, I want to share something I found mildly amusing. The paper is published in the Journal of Web Engineering, and if you visit its website, you’ll notice it includes /index.php/ in the URL. I’m not sure why. Is it a deliberate choice? Or is it a sign of questionable web engineering?

It is estimated that up to 97% of websites today use JavaScript, with more than 80% also relying on a library or framework. JavaScript is often used to manage UI state changes within single page applications, allowing users to interact without reloading the entire page. While this can be done manually via the DOM API, it’s often error prone.

Theory

Modern web frameworks wrap the DOM API and provide a custom declarative syntax. This means that application code can simply describe the desired UI state, and the framework automatically generates the necessary DOM API calls to reflect that state in the browser.

When using the DOM API directly, the amount of script execution required to update the UI scales linearly with the complexity of the change. However, when DOM API calls are generated dynamically by a framework, the framework must first determine exactly which updates are necessary, introducing additional overhead. Furthermore, the chosen rendering strategy can greatly affect the number of DOM API calls made. A bad strategy may result in noticeable delays even for small updates, which is why it makes sense to compare the strategies used by major JavaScript frameworks.

JavaScript versus WebAssembly

The study looks at five popular frameworks: Angular, React, Vue, Svelte, and Blazor. All are JavaScript-based except for Blazor, which uses WebAssembly. Blazor applications are written in C# and run in a .NET runtime compiled to WebAssembly. Because WebAssembly modules lack direct access to the DOM, they rely on an additional JavaScript interoperability layer, which can introduce extra overhead.

FrameworkVersion
Angular11.2.3
React17.0.1
Vue3.0.7
Svelte3.35.0
Blazor5.0.3

Virtual DOM versus data bindings

All five frameworks follow some variant of the Model-View-ViewModel (MVVM) pattern. In MVVM, developers define components that bind an application’s data sources to views, so that changes in the data are automatically reflected in the UI.

Each framework continuously tries to keep the state of the DOM tree synchronised with the state of the component tree defined in application code. This is done using two distinct methods.

The first method, known as virtual DOM (vDOM)-based rendering, is used in React, Vue, and Blazor. It works by comparing the two trees and calculating the minimum set of changes needed to transform one into the other. This generally has a time complexity of O(n3)O(n^3), but can often be simplified to O(n)O(n) by making assumptions that usually apply in browser applications.

The second method is used by Angular and Svelte. Here, there is no separate step for calculating all required changes to the DOM. Instead, each component directly updates its corresponding section of the DOM by tracking the values of its data bindings.

From a performance standpoint, using a virtual DOM can introduce overhead not present in a binding-based rendering strategy.

Creating versus updating

All of the reviewed frameworks perform DOM updates within a render loop that walks through the component tree. The cost of this render loop depends on the size of the input and fixed costs.

A render loop involves two types of work: creating new elements and updating existing ones. Creating elements is generally straightforward and costs the same for each framework, regardless of rendering strategy.

Updating existing elements is where the rendering strategy makes a noticeable difference. Angular, for example, always walks through the entire component tree, resulting in a lot of unnecessary work when only a small part of the tree needs updating. React and Blazor walk only through the subtree of the component that initiates the render loop, which is usually more efficient but may still require some unnecessary work for descendants whose output has not changed. Vue and Svelte, on the other hand, process only “dirty” components whose output has changed. This requires the framework to track which components are dirty. Vue does this at runtime, Svelte handles it at compile time.

Finally, component output can be classified as either static or dynamic content, with static content remaining unchanged after the component’s initial render. Frameworks that can optimise for static content may have a performance advantage over those that cannot.

Practice

Several benchmarks were conducted using Angular, React, Vue, Svelte and Blazor. The authors found significant differences in performance between the frameworks, especially as input size increased. Svelte emerges as the clear winner, outperforming the others across literally every benchmark.

Svelte is the fastest framework when creating nn static elements, while React is generally among the slowest:

nAngular (ms)React (ms)Vue (ms)Svelte (ms)Blazor (ms)
10032113
50099328
100016116313
50008577281461
100001772004724123
250008449569563371
500002520355917398964

Svelte is also the fastest when creating nn components arranged as a binary tree. However, in this case, Blazor is the slowest by a considerable margin:

nAngular (ms)React (ms)Vue (ms)Svelte (ms)Blazor (ms)
12820716317
5127532531059
1024120558422128
409621613722383485
8192297233313142966
163844693944852331870
327687747338974823644

When updating the root component of a tree with nn components, we clearly see how Vue and Svelte benefit from binding-based rendering strategies:

nAngular (ms)React (ms)Vue (ms)Svelte (ms)Blazor (ms)
12837<1<13
5121223<1<13
10241442<1<12
40963292<1<13
81923292<1<13
1638443211<1<12
32768103379<1<13

When updating a leaf component in a component tree of nn components, Angular is the only framework that lags slightly behind. This is because it performs the same amount of work regardless of what has actually changed:

nAngular (ms)React (ms)Vue (ms)Svelte (ms)Blazor (ms)
1283<1<1<11
51213<1<1<11
1024141<1<11
4096334<1<13
8192333<1<15
16384445<1<14
327681044<1<18

Finally, the table below shows the script execution time when updating the entire component tree of nn components, where each component contains primarily static content:

nAngular (ms)React (ms)Vue (ms)Svelte (ms)Blazor (ms)
12843420228
25684432360
5121766425101
1024271017210250
2048292359120502
409644289149541020
8192238841311802013

Overall, the results are in line with what would be expected given the characteristics of each rendering strategy.

The WebAssembly-based Blazor shows significantly worse performance than its JavaScript-based competitors. However, from these benchmarks alone it’s impossible to determine whether this is due to Blazor itself or a fundamental limitation of using WebAssembly for this purpose.

Meanwhile, Svelte demonstrates three key characteristics that likely contribute most to improved performance:

  • The use of a reactivity system to automatically detect dirty components
  • An optimising compiler that generates component update code which ignores static content
  • A binding-based rendering approach rather than a virtual DOM

Summary

  1. Svelte’s on fire, yo