To say that there are quite a few JS frameworks to choose from would properly be the understatement of the year.
It seems like every few months a new, revolutionary framework enters the ecosystem with promises of unseen performance and/or ease of use.
It's gotten to the point where we almost need a framework to choose our next framework (Yo Dawg).
Although, as nice as it would be for a developer to be able to take a vacation and come back without having to learn yet another framework, we also kind of dig it.
New frameworks often bring innovation to the JS ecosystem. This can be in how we build apps and/or how performant they are.
These performance improvements help push the boundaries on what is possible - and as a result, also help inspire other frameworks to implement similar solutions.
But now for the fun question - is there really that big of a performance difference across the different frameworks? This is the question we wanted to answer.
However, as the quite gruesome saying goes: "there's more than one way to skin a cat" 🙀
As the title of this post might already have spoiled, we chose to test which framework was the fastest when it came to SSR - Server-Side Rendering.
The results were interesting, but just like Formula 1, it was often milliseconds that separated the good from the great.
But before we get into the nitty-gritty, first a very big disclaimer.
🔔 DISCLAIMER - YOUR MILEAGE MAY VARY!
As anyone who has ever run any kind of performance test can tell you - results can vary. You can try your best to make the conditions as uniformly as possible and still get different results.
We have tried to set up each demo similarly to the others. However, there may have been a better way of doing this in one particular framework that we were not familiar with at the time.
If you feel something could have been different and/or better, please drop a comment or send us an email at info@enterspeed.com. The source code for each demo can be found in our demo repo: https://github.com/enterspeedhq/enterspeed-demos
Meet the 6 contestants
The 6 contestants we chose, are a mix of some of the most popular frameworks and some of the newer more hyped ones.
The frameworks we tested were:
- Astro: 18,2k stars on Github, created March 2021
- Gatsby: 53,4k stars on Github, created May 2015
- Next.js: 91,8k stars on Github, created October 2016
- Nuxt 3: 8,7k stars on Github, created March 2021
- Remix: 19k stars on Github, created October 2020
- SvelteKit: 10.1k stars on Github, created October 2020
All 6 frameworks were set up using SSR.
What is SSR?
SSR stands for Server-Side Rendering and is a rendering strategy that converts your application into HTML on the server.
Some other rendering strategies are:
- CSR - Client-Side Rendering, which renders in the browser.
- SSG - Static Site Generation, which generates the HTML on build (and therefore only fetches data once).
- ISR - Incremental Static Regeneration, which is a combination of SSG and SSR that allows you to create or update static pages after you’ve built your site.
Unlike traditional SPA's (Single Page Application) which use CSR to render their content, SSR gives a faster "time-to-content" and is better for SEO, since crawlers will see the fully rendered page.
What we build
Our demo site which we replicated for each framework is a beautiful blog containing 6 blog posts.
It uses Tailwind for styling and fetches its content from Enterspeed (a high-performant data store).
Bonus info - all thumbnails were generated using Dall-E 2 with these phrases:
- "A cat driving a formula 1 car digital art"
- "A Llama flying a fighter jet, pixel art"
- "A cool, fearless bee riding a motorcycle"
- "A falcon flying through outer space in a photorealistic style"
- "A person outrunning a cheetah, digital art"
- "A photo of a teddy bear riding a rocket"
All demos are public and hosted on Netlify:
- Astro - https://enterspeed-astro.netlify.app/
- Gatsby - https://enterspeed-gatsby.netlify.app/
- Next.js - https://enterspeed-nextjs.netlify.app/
- Nuxt 3 - https://enterspeed-nuxt.netlify.app/
- Remix - https://enterspeed-remix.netlify.app/
- SvelteKit - https://enterspeed-sveltekit.netlify.app/
The GitHub repo for all the demos can be found here: https://github.com/enterspeedhq/enterspeed-demos
What we measured and how
To measure the SSR performance of our JS frameworks we used Google Lighthouse (here Web.dev/measure). We ran each audit 5 times and calculated the average for each metric.
💡 An explanation of each metric (and acronym) will come further below.
Google Lighthouse measures Core Web Vitals (LCP, FID, CLS), which has become a ranking factor in the Google Search Algorithm, as well as other web vitals (FCP, Speed index, TTI, TBT, and CLS).
We didn't measure FID (First Input Delay), since this cannot be measured in the lab. However, TBT (Total Blocking Time) correlates well with FID in the field.
CLS (Cumulative Layout Shift) isn't included in the results either, since all the demos scored 0 in this category.
Lastly, we wanted to measure TTFB (Time To First Byte), since it can help measure the web server responsiveness which is highly relevant when it comes to SSR.
To measure TTFB we used hey, where we send 250 requests to each demo and measured the average TTFB.
We noticed that the results fluctuated quite a bit when sending requests to our hosted sites on Netlify, so we chose to run each application locally and measure it that way.
We also chose to reduce the number of concurrent workers from 50 to just 1 since our Nuxt 3 application kept crashing when sending multiple requests.
What does each metric mean?
All metrics (except TTFB) are based on Google's initiative: Web vitals. Google made these metrics to provide unified guidance for quality signals.
✂️ Explanations borrowed from Web.dev
Google Lighthouse Performance score
The Performance score in Google Lighthouse is a score from 0 - 100. It is a weighted average of the Web Vitals score. Each Web Vital is weighted as follows:
- First Contentful Paint: 10%
- Speed Index: 10%
- Largest Contentful Paint: 25%
- Time to Interactive: 10%
- Total Blocking Time: 30%
- Cumulative Layout Shift: 15%
The performance score is grouped into three categories (Poor, Needs Improvement, and Good): Needs Improvement,
- 0 to 49 (red): Poor
- 50 to 89 (orange): Needs Improvement
- 90 to 100 (green): Good
Note: A "perfect" score of 100 is extremely challenging to achieve and not expected.
First Contentful Paint (FCP)
The First Contentful Paint (FCP) metric measures the time from when the page starts loading to when any part of the page's content is rendered on the screen.
Speed Index
Speed Index measures how quickly content is visually displayed during page load.
Largest Contentful Paint (LCP)
The Largest Contentful Paint (LCP) metric reports the render time of the largest image or text block visible within the viewport, relative to when the page first started loading.
Time to Interactive (TTI)
The TTI metric measures the time from when the page starts loading to when its main sub-resources have loaded and it is capable of reliably responding to user input quickly.
Total Blocking Time (TBT)
The Total Blocking Time (TBT) metric measures the total amount of time between First Contentful Paint (FCP) and Time to Interactive (TTI) where the main thread was blocked for long enough to prevent input responsiveness.
Time To First Byte (TTFB)
TTFB is a metric that measures the time between the request for a resource and when the first byte of a response begins to arrive.
The results
Drumroll, please... The results are as follows.
Google Lighthouse Performance score
1. 🏆 Astro - 99,2
SvelteKit - 99
Nuxt 3 & Remix - 98,8
Next.js - 98,6
Gatsby - 95,6
First Contentful Paint (FCP)
1. 🏆 Astro, Gatsby, and Remix - 0,8s
Next.js & SvelteKit - 0,9
Nuxt 3 - 1,1
Speed Index
1. 🏆 SvelteKit - 2,3s
Astro & Remix - 2,8s
Nuxt 3 - 2,9s
Next.js - 3,2s
Gatsby - 5,6s
Largest Contentful Paint (LCP)
1. 🏆 Astro - 0,8s
SvelteKit - 0,9s
Next.js, Nuxt 3, Remix - 1.2s
Gatsby - 1,9s
Time To Interactive (TTI)
1. 🏆 Astro - 0,8s
SvelteKit - 1,0s
Nuxt 3 - 1,2s
Remix & Gatsby - 1,5s
Next.js - 1,7s
Total Blocking Time (TBT)
1. 🏆 Astro - 0ms
Nuxt 3 - 20ms
Gatsby - 28ms
Remix - 30ms
SvelteKit - 36ms
Next.js - 54ms
Time To First Byte (TTFB)
1. 🏆 SvelteKit - 62ms
Next.js - 63 ms
Gatsby - 133ms
Remix - 136ms
Astro - 137ms
Nuxt - 438ms
The conclusion
Now, now, before you start burning down the place, remember what we said in the beginning - your mileage may vary!
Based on our results, it seems Astro really is the new, fast kid in the class - although not as much in TTFB.
However, the TTFB results can also be the development server not showing itself from its best side.
SvelteKit also had some impressive results and is also one of the new frameworks getting a lot of praise when it comes to speed.
Does this mean you should skip the other frameworks and choose one of these two? Absolutely not.
Each framework has its use case and its benefits. We are personally big fans of all the fantastic features Next.js provide.
Moreover, many people use a combination of rendering strategies, e.g. SSG for their homepage, SSR/ISR for their blog pages, and so on.
Therefore, choose the framework that best fits your needs.
🩸🔥 Is your blood still boiling and do you need to calm down? Don't worry, we got you.
What is Enterspeed? 🏎
Enterspeed is a high-performant data store that makes it possible to gain speed & flexibility by combining your services.
Connect and combine all your services into a single API endpoint. Easily transform your data with our low-code editor, to get exactly what you need – all stored in our blazing-fast edge network.
Enterspeed is used in this article to give the data layer (the blog posts) high and consistence performance across all the tests.
Top comments (15)
Nice breakdown of frameworks!
I've always thrown out the performance metric for UI frameworks. What's important to me is how easy it is to build? Are the dependencies stable? Do I need extra build tools? Can I write TypeScript? Can I read my code months later? Oddly enough most frameworks aren't considered because their routing system.
As a beginner in the area, I am very curious on if you know anything about EmberJS with respect to routing. I've read that they have a mature routing system which is one of their main strength.
They also seem mature and chilled kind of framework and slow moving than others (since they are mature) since front-end scene is overwhelming. Convention over configuration rings nice. Their RFC (Request for comments) is really cool compared to others I have been reading up on. Maybe I am wrong here but been taking my free time to understand EmberJS better. Am tied between Svelte and EmberJS. If am to take something new and trending, Svelte seems like the right pick. Especially now that SvelteKit is stable. Would love to pick your brain on this.
Typescript gets officially supported ; their routing conventions inspired; it provides SSR; all in one etc... EmberJS is the very first JS frontend framework introducing CLIs an first SPA framework. Funny fact, their CLI got literally copied by Angular's team.
Used by a bunch of companies among Netflix and Apple.
IMO EmberJS is the way to go to for any beginner. And, keep an eyes on other frameworks to sharpen you skills.
For getting a job out there -- and if you don't care about the quality of the habits you acquire during the first stages of your learning process, pickup Vue or React instead ;)
Yeah, makes sense to focus on developer experience. I'm curious on when in the proces you shift your focus to performance?
Optimization is the lest step for me. When an issue comes up or I notice a performance bug by accident then I'll fix it. Otherwise, I'm more concerned about getting features out the door.
Love it! Seems pretty thorough and generally following the recommended practices for each framework. I especially love how close the results were in the end.
I do want to point out that the rendered pages are entirely static, so Astro really shines here. If you remove the
<Scripts />
element from Remix I imagine the lighthouse score would improve a lot, too (although not the SSR points like TTFB). I don't know if the other frameworks support the same thing. Alternatively, it would be cool to measure the speed of transitions between pages, since that's where frameworks with full hydration can improve the UX (perceived performance). Another alternative would be to include interactive elements on the page and see where Astro ends up compared to others with hydration.NodeJS based; but what about Deno; and Web container ?
Nice article, thanks for sharing !
These kind of articles really motivates me to do some more personal projects and enjoy the coding as it used to be before having a job.
Your work is really inspiring! Thanks for the great article! 🚀
Could you add Qwik to your comparison as well?
It's maybe the hottest modern contender.
I completely agree on what have you written and how you have bifurcated every framework.
But just want to ask, have you heard about TezJS? Did you even try it?
If no, then, I would recommend you go for it. I am sure, it tops the list and gives the best performance on Google Lighthouse.
Try TezJS (GitHub - github.com/tezjs/tezjs) and then let the world know about it through this article.
Happy Coding! :))
Ouch, TTI (Time To Interactive) between Astro and Next.js, more than a second slower, that hurts ... TTI is arguably one of the most important indices. And, not quite coincidentally (?) - Next.js is the oldest framework of the bunch :)
However, isn't it a bit misleading to say you measured the SSR performance of the frameworks? I'd say you measured the total web performance, and the SSR process is an important part of that (but I think it's not the only part).
In the test, I chose to only focus on SSR and not use SSG or CSR for any of the sites. Therefore all the sites are rendered using SSR. Had I chosen SSG the results may had be different. I don't see it as misleading, but appologize if it comes across as this.
I agree that "misleading" is putting it a bit harsh - my bad! :)
However to me, "measuring SSR", when taken literally, would mean profiling or measuring the server side rendering process (i.e. the execution of the React rendering process on the server, rather than on the client), which is a very specific process, that's pretty heavy on the server - so assessing the performance or efficiency of "SSR" as such ...
However your results - the differences between the frameworks - are probably due to much more than to the differences in efficiency of "SSR" per se, in each framework.
Yeah, nitpicking, I know ... :)
All of which is not to take away anything from what you've done, which is great!
"End-to-end performance with SSR" vs. "isolated SSR performance" could be a way to delineate it.
Nice article! If you consider adding other frameworks to this comparison could you add Quasar as well? Vue framework with 21k github stars, would love to see where that would stand in the rankings.