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.