DEV Community

Cover image for JavaScript Framework TodoMVC Size Comparison
Ryan Carniato for This is Learning

Posted on

JavaScript Framework TodoMVC Size Comparison

Size in JavaScript Frameworks is actually a pretty tricky thing to estimate.

Even nailing down the size of runtimes is unclear. You might go to bundlephobia.com but the size there can be misleading. With tree-shaking not all code is included. Bundlephobia also doesn't include sub-modules. Things like svelte/motion or preact/hooks.

There isn't only the size of the runtime but the size of the component code. Not all components are equal. Templates in each framework compile differently. Evan You, creator of Vue put together a comparison between Svelte and Vue which was pretty illuminating.

I decided to take his process and methodology and apply it in addition to Preact, React, and Solid. So let's begin.

I'm the author of Solid, so be aware of that in terms of any potential bias I might show. I'm trying to keep things as equal as I can.


The Setup

The test looks at the size of the library(vendor) bundle and the component code for TodoMVC. Every framework has a version and the requirements are well laid out so each is relatively the same.

I first looked at using only official demos, but Preact and React still use class components that are larger and not as representative of modern development. This did mean an increase in library size for Preact(3.81kb -> 4.39kb), which doesn't contain hooks as part of its main package but is definitely worthwhile for this test. In the end, I couldn't find a Hooks implementation I was happy with so I rolled my own implementation which I based off Solid's. You can find all the implementations here.

Vendor chunks are pulled from Vite which supports all tested frameworks. For component code I used Vue, Svelte, Solid's REPLs and Terser REPL to minify. For Preact and React I used rollup to generate the compiled code.

This process is much less rigorous than the benchmarks I typically do. Honestly coding style and available demo code put in a reasonable amount of variance. But I think it is still approximately in line.


Results

First step is to get the size of the component and vendor code for each. TodoMVC is a pretty reasonable example as it involves basic state handling, conditional and loop rendering, forms, and even serialization to local storage. In so we get a much better idea of what the base size of each framework is than Bundlephobia.

Preact React Solid Svelte Vue
component size (brotli) 1.21kb 1.23kb 1.26kb 1.88kb 1.10kb
vendor size (brotli) 4.39kb 36.22kb 3.86kb 1.85kb 16.89kb

In general, mutable is smaller than immutable state, and VDOM-less libraries generate more JavaScript for their templates. Vue's components generate the least code, edging out the JSX libraries and Svelte

Svelte's runtime really is small at 1.85kb. Preact core might be smaller than Solid, but with hooks in tow, the reactive framework ends up being the tinier one.

From this, it is easy to calculate the size of each framework at N number of TodoMVCs + vendor chunk.

1 5 10 20 40 80
Svelte 3.73kb 11.25kb 20.65kb 39.45kb 77.05kb 152.25kb
Solid 5.12kb 10.16kb 16.46kb 29.06kb 54.26kb 104.66kb
Preact 5.60kb 10.44kb 16.49kb 28.59kb 52.79kb 101.19kb
Vue 17.99kb 22.39kb 27.89kb 38.89kb 60.89kb 104.89kb
React 37.45kb 42.37kb 48.52kb 60.82kb 85.42kb 134.62kb

While Svelte starts the charge in the lead, it is quickly overtaken by Solid, who passes the crown on to Preact. Preact is the smallest for a good chunk of the table before ultimately Vue is.

So putting the inflection points in a table:

Svelte Solid Preact Vue React
Svelte - 3.2 3.8 19.3 52.9
Solid - - 10.6 81.4 1078.7
Preact - - - 113.6 -
Vue - - - - -
React - - - - -

This is the point each framework gets larger than the next. From 0-3 TodoMVCs, Svelte is the smallest. From 3 to 10 Solid is the smallest. 10-113 TodoMVCs Preact is. And more than 113 TodoMVC's Vue is.

Preact and Vue never intersect with React, and even for Solid that does it is only after about 1080 TodoMVC's. All in all this is pretty consistent with what we see in demo's and benchmarks. Svelte is always smallest for the Hello World's and TodoMVCs, Solid for the "Real World" demos and the types of simple sites people build on streams, and Preact for things on the larger side.


Analysis

TodoMVC as a single component is on the larger side and typical implementations do it in 3-4 components so I wouldn't view these component numbers necessarily to be the number of components. But it is easy to see each framework has its sweet spot.

Size's biggest impact comes during the initial page load. Other code split routes can be lazy-loaded as desired, but initial page load is a cost that every site takes upfront. If you subscribe to the thinking present in Addy Osmani's The Cost of JavaScript series and Alex Russell's Can You Afford It?: Real-world Web Performance Budgets we really should be aiming to keep initial page load JavaScript under 130kb.

If this is a SPA that budget includes data fetching, state libraries, and router. It's not uncommon for that to be an additional 20 to 25kb JS with most frameworks. Reactive ones like Svelte, Solid, and Vue may have state management built-in but even then when you consider 3rd party utility libraries for formatting I'd say our framework and component code should be less than 100kb.

At that budget, just how many TodoMVCs does each framework allow for?

React Vue Preact Solid Svelte
10kb - - 4.6 4.7 4.3
20kb - 2.8 12.9 12.4 9.7
40kb 3.1 21 29.4 28.7 20.3
70kb 27.5 48.3 54.2 52.5 36.3
100kb 51.9 75.6 79.0 76.3 52.2

Well at 100kb React and Svelte are actually almost identical. And Vue, Preact, and Solid are right next to each other with almost 33% more budget available to them. But that's the upper end. At 40kb Preact and Solid can deliver pretty heft sites with a similar advantage over Vue and Svelte, at a range React isn't even really an option.

Alex's goal was 5 seconds TTI on a slower device and network. For some industries like eCommerce that target should be more like 3 seconds. 70kb - 25kb = ~45kb budget here. How can a larger library like React even compete?

React Server components carry about ~8kb more on top of React's current runtime already price them out of this conversation. Multi-Page Meta-Frameworks like Astro which remove the need for routing, and possibly other 3rd party libraries, are likely just barely enough. But even amazing tools like Astro bring ~9kb with them when there is JavaScript to load.

But for the other's there is more room to play. A simple site might only 5-10 islands on a given page any option is good there. Even full SPAs for smaller libraries like Preact, Svelte, or Solid are well within a happy range.


Closing Thoughts

In the ranges that matter, all frameworks are pretty comparable. Svelte might ultimately load more JavaScript across many pages for a really large app, but the others are close enough that on the larger side it won't be felt.

Looking across the board Preact is the winner on size still. Solid comes close enough that that difference wouldn't be noticed, but Preact deserves the nod. React is priced out of the smaller targets or performance-sensitive ones but once an app gets large enough its size isn't noticeable. Vue sort lands right down the middle in this range, although ultimately it might send the least JavaScript in a really large app.

Keep in mind this comparison is pretty rough and should be only seen as an estimate. I only regret not being able to put the time in to look at more libraries. Unsurprisingly a lot of TodoMVC examples are written MVC style which is unfair for this comparison or use stores like Redux. I wasn't prepared to write a bunch myself (Preact and React were enough). So this will have to stand.

Hopefully, this gives you enough to think about until the next time the conversation of size comes up. Or maybe by then, it will be a completely different conversation given the way Marko and Qwik have been completely shattering the way we measure JavaScript Framework code being sent to the browser.


Full source for the TodoMVC examples used in this article found here

Discussion (14)

Collapse
bcowley1220 profile image
Brendan Cowley

I have to relay that while the payload size is important, as a converted evangelist to Svelte, I can't pass by a chance to say that my dev satisfaction with Svelte is off the charts. I'm aware that this is totally relative, but I don't care! Dev satisfaction is something that isn't even on the radar these days.

Collapse
ryansolid profile image
Ryan Carniato Author

It's not unusual. When I do a performance article often someone points out how ecosystem or DX or jobs are the undervalued thing we are missing. But those are always the most commonly talked about things from my perspective. Performance is something that generally gets shelved for these other priorities which is why it's important to bring awareness.

My feeling is that dev satisfaction in JavaScript is on the radar more these days than it ever has, often at the cost of performance. Which is probably why I sound like a broken record. So much so that we've seen the rise of more developer advocates and DX specialists in companies even.

I think what we have is an abundance of good choices. Svelte is likely a high, but a lot of people are really really satisfied with React, Vue, and other popular options. Sure people can always complain about what they are forced to use because of their jobs, but on the DX side things have only been getting better. I think the challenge is things like SSR are offering new territory and complexity we haven't figured out the best patterns for. But in general we've seen a maturing in the JavaScript ecosystem that has made it more accessible than ever before.

Collapse
vegegoku profile image
Ahmad K. Bawaneh

The question is. Is such a small application a good way to measure how such frameworks perform for such comparison? In many many cases the actual differences starts to grow at large scale. When there is a lot more envolved than a few lines of code.

Collapse
ryansolid profile image
Ryan Carniato Author

Not in the sense that compression works better with bigger chunks so this might be a bit conservatively pessimistic on component code size. However this applies to all.
TodoMVC is also fairly dense which is good for this and fairly uniform. Most larger uniform demos like HackerNews or Realworld demo have alot more static view code which emphasizes this more. If I recall the component code for each page in Solid's HN demo is almost half the size of Svelte for example. These examples of course are less uniform making more work to compare.
They also pull in data fetching and routing. Which effectively grow the vendor chunk. This is even more variable. I know the analysis under represents this additional library code which is why I think we should be targetting smaller component sizes to reach budgets.
Finally if the hope were to look at real sites in the wild. In a sense that would be best but the sample size would need to be enormous and need to figure out how to be representative not to be unfair to more popular frameworks used in less optimal ways.
Basically like with any microbenchmark this has limitations but artificially forces to the surface a measurable trend.

Collapse
vegegoku profile image
Ahmad K. Bawaneh • Edited on

Yes exactly, well answered.
I am coming from the background of building large enterprise apps and I know large apps are different. The current app I am building right now is around 100k loc and has over 200 screens, the JS bundle over the wire jziped is still under 900kb which great. I am not yet in the area where I need to think of code splitting.

Collapse
jfbrennan profile image
Jordan Brennan

Would love to see Riot.js in the comparisons. It’s a big contender having a small (6kb?) runtime and no vdom.

Collapse
jonrandy profile image
Jon Randy • Edited on

Beat me to it. It's also a joy to work with

Collapse
ryansolid profile image
Ryan Carniato Author

One of the normalizing things I did for this comparison was use Vite to do the build. Riot's SFC format is perfect for the comparison, but I haven't seen a Vite plugin as of yet.

Collapse
ninjin profile image
Jin • Edited on

I've done these measurements with $mol-based implementation: todomvc.hyoo.ru/

It uses a radically different approach to build. For example, there is no separation into vendor and application bundles. In addition, styles, scripts and some images fall into a single bundle.

By the way, it would be necessary to add not only the size of JS to the measurements in a good way, but also the size of CSS, since it is no less important and strongly depends on the techniques used in the framework for working with styles. Oh well.

The total bundle of $hyoo_todomvc without minification and with gzip compression weighs 27.6KB. Let's assume that after minification, cutting out styles, images and compressing brotli, this size will be reduced to 21.5KB (online tools failed, and I'm too lazy to start it all locally, but let's take a very conservative estimate). If we throw out the reused code, styles and images, then the remaining pure JS code $hyoo_todomvc after minification and brotli weighs 1.5KB (online tools have coped here). That is, the non-specific code for $hyoo_todomvc is <=20KB.

We get the following picture:

1 5 10 20 40 80
$mol 21.5 27.5 35 50 80 140

It seems to be a lot. However, the $mol implementation does a bit more than the rest:

  1. Rendering is automatically virtualized, which gives fast operation even of huge task lists. Virtualization in other frameworks will make them much heavier. And as the application grows, virtualization will inevitably be required.
  2. $hyoo_todomvc is a self-contained but fully controlled component. Adding a comparable number of extension points to other frameworks will make the component heavier by at least an order of magnitude.
  3. And other pleasant little things. Like remembering scroll positions, showing errors in emergency situations, graceful log and debug etc.
Collapse
lil5 profile image
Lucian I. Last • Edited on

I wonder how alpineJS does on the todoMVC

Collapse
ryansolid profile image
Ryan Carniato Author

Alpine wouldn't be an equivalent comparison. The template in the HTML would require a different way of measuring size.

I did implement Alpine in the JS Frameworks Benchmark and one thing I noticed was the base framework size was about 16kb. It isn't really built for treeshaking (github.com/alpinejs/alpine/discuss...).

I think for Alpine the approach of HTML template is going to limit you to smaller applications any way (intentionally), so assuming the best case I'd picture it similar to Vue except you probably wouldn't go past 40kb.

Collapse
lil5 profile image
Lucian I. Last

I appreciate the well written comment.

I have seen "large" projects where the largeness was take up by php and used minimal to none javascript on top of using an overkill framework.

Thread Thread
ryansolid profile image
Ryan Carniato Author

I should have clarified. I meant large JavaScript rendered projects, since that is what we are measuring here.

Collapse
siddharthshyniben profile image
Siddharth

I should make a TodoMVC for Sleek.