NEW

Prismic offers an ideal solution to feature your e-commerce products in your promotional landing pages or inspirational content. View more

Unraveling the JavaScript Meta-framework Ecosystem

Written by Ben Holmes in House foundations icon. Foundations on July 06, 2022
Next.js SvelteKit Nuxt Remix

It's so easy to build a website that it's never been harder.

~Me, start of 2022

Whether you're new to the JavaScript ecosystem or a seasoned veteran, you know just how diverse the "getting started" landscape has become. There are dozens of frameworks and "meta-frameworks" available to spin up your next project, each catering to different needs and team communication styles.

This isn’t a generic listicle on what meta-frameworks exist. We're focusing on the motivations and use cases behind stable and emerging meta-frameworks in the field, plus the underlying tooling worth looking out for as you cross-shop 🛍

What is a meta-framework?

I snuck that little phrase into the intro, didn't I? Well, as the name implies, a meta-framework is a system one level above that stitches multiple frameworks together.

React is a great starting point here. We'll ignore the whole debate on whether React is a "library" or a "framework" for now, since many refer to React as a UI "framework" these days. This framework is built for an important task: taking a blob of data or "state," and rendering an interface to the screen to display and update that state. This leaves a whole host of web development needs unaccounted for, including:

  • routing for pages across your site
  • styling those interfaces with CSS
  • creating backend servers and/or serverless functions to persist data
  • creating shared "layouts" between pages

... and so on. Sure, some developers prefer to cobble together different libraries and frameworks to fill in these gaps (the MERN stack was a go-to back in college). But others would prefer an all-in-one solution to make some or all of these decisions for them.

Enter meta-frameworks. You may have heard names like Next.js, Nuxt.js, and Angular come to dominate this category. Each of these addresses common concerns like routing, layouts, serverless backends, and CSS bundling with out-of-the-box solutions.

So with all these options ... which meta-framework do we start with?

TL;DR

Alright, so you want a quick answer on which meta-framework to choose. Here’s my watered-down snapshot of the meta-framework buffet as of 2022:

  • If you want the most popular, safe, and stable option: try Next.js.
  • If you're building a website where load times, performance, and developer flexibility are key, try Astro.
  • If you're an old-guild developer that demands web-standard forms and cache headers, try Remix.
  • If you don't like React, try Nuxt and SvelteKit.
  • If you think React’s hooks and performance are problematic, but you still like JSX, try SolidStart.

☝️ And if you want to know the “why” behind these answers, read on!

Next.js is dominating the space

If this is a popularity contest, Next.js is the runaway pageant queen. Its npm installation numbers have exploded in recent years. Backed by the venture-funded hosting platform Vercel, Next.js has the resources to crank up these numbers even further 📈

This isn't too surprising when looking back through React's history. Next.js was among the first "meta-frameworks" built around the tool, with some clear innovations right out of the gate:

  • Every file you create is a "route" on your website. No need to wire up fancy routing solutions like react-router.
  • Every page can fetch server-side data. This makes talking to databases much easier while offering server-side rendering solutions (SSR) to generate static HTML for faster page loads.

Since then, Next.js has taken over the backend of your website as well. By including JS-based API routes in your project, you can whip up serverless functions to handle user requests and fetch from a database with minimal backend knowledge required. As you might imagine, the Vercel hosting platform makes Next.js deployment as simple as possible too.

Next.js is pushing React forward

There's a reason Next.js' package description is simply "the React Framework." In fact, the React core team has come to prescribe Next.js as the best starting point for production-ready projects (see their new beta documentation).

It's also being used as stomping grounds for newer React features. React Server Components were notably announced and documented in Next.js before being explained on React's own docs 😳 This is because newer React features are tough to appreciate "in the raw." I encourage you to read up on server components if you're diving deep into the React landscape. But in short: server components offer a new way to render server-side information quickly and efficiently. Since Next.js puts the "server" in server components, it becomes the perfect companion to learn about this architecture.

In sum, this vote of confidence from the most popular UI framework, combined with Vercel's own success as a business, make Next.js a very safe bet for your next project (pun intended 🙃).

Remix is pushing Next.js forward

We can't talk about Next.js without mentioning the dark horse in the race: Remix. This meta-framework is built by the dynamic duo behind react-router, one of the first (and most popular) solutions to page routing in React. This gave creators Ryan and Michael a lot of perspective on what React could do.

This perspective led to some grievances with Next.js:

  1. Sending data back to a server is hard. Also called "data mutation," this mainly concerns sending data from a form back to the server. Next.js helps you with the R in CRUD, but not the rest!
  2. There was (at the time!) no nested routing. Every route in a Next.js app was standalone, with only a single "shared" component wrapping each route for shared information storage. This was limiting for more complex dashboards, where nested navigation and nested data fetching are the norm. You can see an interactive example of just such a dashboard in Remix's nested routing explanation.

As you may have noticed, that second feature has already inspired Next.js' Layouts RFC (request for comments) to bring nested routing to the incumbent React meta-framework.

But what about that first piece, the "data mutation" problem? This is addressed by a pretty important distinction: Next.js is built to deploy either server-driven apps or static websites, while Remix is exclusively server-driven. This means you'll need a server or serverless host to deploy any Remix app you build. But Remix has arguably flipped this limitation into a strength by embracing:

What do these features have in common? They're all covered in the MDN docs! In other words, they're all built into the web platform, not the proprietary meta-framework.

This speaks to a general trend emerging in meta-frameworks: the web platform is getting good, so frameworks have fewer and fewer pieces to build in-house. The ESM standard is another piece to this puzzle, which brings us to...

Why Vite is powering the meta-frameworks of the future

Vite is an important tool in the meta-framework landscape. In fact, both Vite and the companion tool, esbuild, topped the libraries list for the 2021 State of JS survey 👀 No, Vite isn't a meta-framework on its own, though it is the underpinning of countless meta-frameworks emerging in the space, like Nuxt, SvelteKit, and Astro (more on these in a bit).

So what is Vite? In short: Vite is a website bundler for your JavaScript, CSS, static assets, and anything else you are transforming and optimizing on your website. I'll quote one of my previous articles on Vite to break this down:

"Remember Apple’s original value proposition for the iPhone, 'An iPod, a phone, an Internet communicator…'? I see Vite as a similar proposition, as it bundles three related tools into one:

(Source article on the LogRocket Blog)

☝️ That "dev server" piece is the biggest departure from tools like Webpack and Rollup. By listening to your dev server, Vite doesn't have to bundle your website top-to-bottom before it's ready to view in the browser. Instead, Vite will wait for you to visit a given route (say, your /about page), and transform any resources that page requests on-the-fly. This means only building what you see, while smartly ignoring the rest ⚡️

ESM makes this possible

Have you ever thought about what import statements really do?

 
// src/main.js
import { dripCoffee } from './coffee-shop-where-im-writing-this.js'
import { charger } from './my-bag.js'

doWork(dripCoffee, charger)

If you were using a tool like Webpack back in 2015, you would probably build this file to some output bundle like so:

 
// dist/main.js
var dripCoffee = {...}
var charger = {...}

doWork(dripCoffee, charger)

There are a few pieces to note here:

  1. Any imports for npm packages or local files would be in-lined in some way.
  2. Your browser-ready HTML file would point to dist/main.js.
  3. Webpack would re-build a new dist/main.js whenever src/main.js was edited.

That first piece is crucial. In the early days of npm packages like React or Vue, import wasn't even a concept a browser could understand! This meant wacky bundling processes to transform developer-friendly imports into browser-ready vars.

The platform decided this bundling shouldn’t be necessary. Once every major browser adopted the ES Module standard, import was ready to use everywhere 🥳

Meta-frameworks like Next.js and Nuxt.js took advantage of this early on via code splitting and dynamic importing. Still, it required rebuilding to a dist folder on every file change. You might think you'll always need that dist folder, but Vite says... what if you didn't? 😳

Say we're loading that src/main.js from earlier onto our index.html page. With Vite, we can load src/main.js directly like so:

 
<script src="src/main.js"></script>

If we head to http://localhost:3000/src/main.js in our browser, we'll see something pretty familiar (go try it in this sandbox!):

 
import { dripCoffee } from '/src/coffee-shop-where-im-writing-this.js';
import { charger } from '/src/my-bag.js';

doWork(dripCoffee, charger);

... that's right, our file is completely untouched. Vite leans on ES module support heavily during development, so it can avoid all the time-consuming bundling and show your changes instantly. For instance, editing /src/my-bag.js will only reload /src/my-bag.js, and editing /src/unrelated-file.js won't trigger any reloads at all.

Vite gets a bit smarter for "exotic" file extensions like .ts for TypeScript, .scss for styles, or .jsx for components. By owning your development server, Vite can intercept exotic imports and transform them to something the browser understands on-the-fly ✨

Here's a high-level diagram of how Vite handles your assets:

An explainer diagram that shows src/main.js importing a regular JavaScript file without any issues. On the other side, src/main.js tries to import a TypeScript file, which the browser doesn't like, so Vite intercepts the request and turns it into a JavaScript file that the browser will accept.

Why should I care?

Alright, so why did we learn about ES Modules for the past 3-4 reading minutes? There's 2 major reasons:

  1. It further proves how the web platform is getting good. Remember how Remix made forms and caching cool again? Well, native support for imports makes less bundling pretty cool too. This means meta-frameworks are getting simpler and less proprietary, leaning on web platform standards that all meta-frameworks can share.
  2. It demos how batteries-included Vite can be for meta-framework authors. By managing the dev server, supporting exotic extensions out-of-the-box (TS and JSX to name a few), and optimizing production builds, you can build the next ahem Next in no time 👀

Vite and the NextJS disruptors

🏆 nominated for “nerdiest band name” ever

Vite has sparked a flood of new meta-frameworks in the past couple of years, each poised to take over your website / web app’s tech stack. Let’s hit the highlights.

Astro

Astro is a meta-framework that takes flexibility to the extreme. Every route on your site starts with a .astro file, which is basically "HTML with import support." From here, you're free to load React components, Vue components, Svelte components, and more, with "directives" to manage if and when their JavaScript resources are shipped to the browser. This means little-to-no JavaScript to start (fast load times), with opt-ins for interactivity where it counts (app-like functionality). This is best shown through an example:

 
---
// static text with zero clientside JS
import HeroBanner from '../components/HeroBanner.astro'
// dynamic nav bar written in Vue
import NavBar from '../components/NavBar.vue'
// dynamic image carousel from a helper package
// Might be React, Vue, Svelte, etc
import ImageCarousel from 'fancy-image-carousel-package'
---

<html lang="en">
...
<Hero />
<!--ship JavaScript for screen sizes 600px and below-->
<NavBar client:media="(max-width: 600px)" />
<!--ship JavaScript when the carousel scrolls into view-->
<ImageCarousel client:visible />
...
</html>

Vite is the secret sauce to let Astro support every "flavor" of component framework under the sun. This flexibility is also a serious leg-up over tools like NextJS for distributed teams. Does your admin dashboard team prefer React, while your docs team prefers Vue? Just install the integrations for each and get to dev-ing.

There are some unique benefits for server-side rendering and optimizing load times as well. For these, I encourage you to check Astro's super detailed documentation guides 🚀

Nuxt 3

Nuxt is the first Next.js alternative I became aware of. It started with a simple goal: match all of the benefits we discussed for Next, but with Vue components instead of React.

Since then, Nuxt has found a few unique benefits over Next that Vue + the Vite bundler help make possible. To name a few:

  1. All your project components and layouts are global. This means all your .vue snippets are available in your routes without import statements 😳
  2. Fetch requests have superpowers. Nuxt supplies data helpers like useFetch to manage transforming JSON responses, auto-wiring refresh buttons, managing error states, and more 🪄
  3. Nested routing is baked-in. See the Remix section above on why this kicks a**.

SvelteKit

SvelteKit is another Vite-powered meta-framework worth trying out, this time trading React components for Svelte.

This is probably the greatest Vite success story I could point to. A couple of years ago, the Svelte team was struggling to maintain their then-Next-competitor, Sapper. Rich Harris (resident Svelte overlord) felt Sapper's patterns were a bit too close to NextJS, with few competitive advantages to make Svelte shine.

So, Rich made the tough decision to build a new meta-framework from scratch. This would have been a pretty huge undertaking using raw tools like Webpack. But by leaning on Vite's built-in dev server and asset management, Rich had a working version of SvelteKit after just a few months of hacking 👀

It brought some major advantages over Next out of the gate:

  • Bundles are much smaller on average. Even with SvelteKit's comparable feature-set, a SvelteKit app tends to undercut Next.js by a wide margin. This comes down to Svelte's diminutive size-per-component compared to React. I recommend Rich Harris' "Rethinking Reactivity" talk to understand the benefits here 🧡
  • Deployment is simplified with adapters. This lets you install a helper for your favorite hosting platform (Vercel, Netlify, etc) and hit "deploy," no added config necessary. This model has inspired players like Astro and Remix as well.

There are also the advantages of Svelte itself over React, including built-in solutions to scoped styling and page transitions or animations.

A cartoon graphics space scene showing an astronaut exploring space as rockets and planets float around.

Stay on the leading edge of development with a monthly coding challenge.

Explore new tech with a fun, scaffolded, coding challenge that lands in your inbox once a month. You'll get all of the learning with minimal spin-up.

Solid Start

Solid Start is yet another Vite-powered meta-framework, now trading React components for SolidJS. This project is still in its early stages as of this writing, so I won't dig into the evolving feature-set too deeply. The main takeaway: SolidJS is a React competitor that needed a meta-framework for building websites, and Vite provided the perfect backbone yet again ❤️

Comparing the UI frameworks behind these meta-frameworks

If you’re coming from NextJS, it’s worth comparing its underpinning (React) to the other component frameworks behind these Vite-based options. If you want to play with each of these before “picking a side,” Astro is an excellent stomping ground!

  • React vs. Vue (basis of Nuxt.js) - To give a blanket, super-imperfect-super-anecdotal overview: React typically suits those with backend programming or JS-heavy backgrounds, while Vue better suits backgrounds in HTML and CSS, old-school web development patterns like Angular 1.x, and/or design. If you want to fight me on this, my Twitter handle is @bholmesdev 😉
  • React vs. Svelte (basis of SvelteKit) - Just take the last bullet and replace Vue with Svelte! Vue and Svelte are both template-first languages that should appeal to developers in similar ways.
  • React vs. SolidJS (basis of SolidStart) - These frameworks are actually pretty similar in their intended audiences, since both are component-based frameworks written in JSX. The real SolidJS benefits come from a) performance gains, and b) a simpler mental model for state management. I recommend Solid's own framework comparison guide for more.

Conclusions

We've covered a lot of ground in the meta-framework space. I hope you take away some major themes:

  1. The web platform is getting good. This is making meta-frameworks faster, simpler, and increasingly similar to each other with common tooling like Vite.
  2. NextJS is king, but others are catching up. Vite makes building a new meta-framework easier than ever. This means more options for developers and more niche use cases and team preferences getting filled.
  3. SSR is on the rise (again). Remix is showing how powerful our apps can be if we trade static site generation for cache-laden servers. Plus, with the concept of "adapters" that SvelteKit introduced, deployment to these servers and serverless functions has never been easier.

So, what do I pick?

I won't pretend there's a "silver bullet" in the meta-framework space that'll suit everyone's needs. So, I encourage you to bookmark that TL;DR we kicked off with for a snapshot of the options that I’m comfortable recommending.

That said, if you're a weekend hacker that sat through this massive blog post, try all of them before deciding 🙃

A black and white portrait of Ben smiling.

Ben Holmes

Frontend dev exploring how the web works. Creator of @slinkitydotdev, full-time dev at Astro.

More posts