Skip to content

Chapter 15: The Meta-Framework Era

Single-page applications solved one set of problems and created another.

The previous chapters described how SPAs gave the web continuity, made interfaces feel fluid, and let the client own routing, state, rendering, and interaction. By the late 2010s, the costs of that model were impossible to ignore. Large JavaScript bundles. Slow initial loads. Empty HTML pages waiting for the framework to bootstrap before anything was visible. SEO that broke when JavaScript failed to run or ran too slowly. Social media previews that couldn’t read the page. Search engines that crawled the markup and found a single <div id="root"> and a script tag. Hydration overhead. Cache invalidation problems. Duplicated server-and-client logic. Production deployments that depended on a dozen build tools agreeing about how to package everything.

The frontend had become an application platform. It needed more than a component library to ship serious products on.

It needed a framework around the framework. That’s the meta-framework era.

Next.js, Nuxt, Remix, SvelteKit, Astro, SolidStart, TanStack Start, and a handful of others emerged between roughly 2016 and 2024 to coordinate the concerns that component frameworks didn’t fully address. Routing. Server rendering. Data loading. Mutations. Forms. Streaming. Code splitting. Deployment targets. Build pipelines. Authentication. Caching. Observability. The list of what a serious frontend application needs grew faster than what any single component library could provide, and meta-frameworks filled the gap.

This chapter is about what the meta-framework era built, who built it, and what the gap it filled tells us about where frontend architecture is going.

Next.js is the dominant meta-framework, by adoption and by influence. The project was started in 2016 by Guillermo Rauch and a small team at Zeit, a deployment-focused startup Rauch had founded in 2015. Rauch had previously built Cloudup, a file-sharing service later acquired by Automattic, and Socket.IO, the real-time-communication library that powered an early generation of WebSocket-based web applications. Next.js was, in its original framing, a React framework that solved the SPA-deployment problem the team kept seeing in its customers’ applications.

The early Next.js story was straightforward. The framework added server rendering, file-based routing, code splitting, and a development-server experience to React applications. A page in pages/about.js automatically became the route /about. Server rendering happened by default. The framework’s getInitialProps (later getServerSideProps and getStaticProps) let pages declare what data they needed and where to fetch it from. The build process produced an optimized bundle of static and dynamic routes that could be deployed to any Node-compatible host.

Zeit renamed itself to Vercel in 2020 and refocused the company entirely on hosting Next.js (and other frontend) applications. The commercial model is now visible: Vercel sponsors Next.js’s open-source development; Next.js applications deploy to Vercel by default; Vercel makes money on the deployment hosting. The relationship is healthy in the sense that both sides depend on the other and neither has obvious incentives to break things. The relationship is also concentrated in the sense that the most-used React meta-framework is owned, governed, and commercially aligned with a single hosting company. The framework’s direction is, in practice, Vercel’s direction.

Next.js has gone through several major architectural transitions. The most consequential recent one was the App Router (2023), which introduced React Server Components, a new file-based routing convention, a new data-fetching model, and a substantial rewrite of how applications were structured. The transition was controversial within the Next.js community for reasons familiar from the AngularJS → Angular 2 parable in Chapter 11. Existing applications used the Pages Router; new applications were expected to use the App Router; the migration path between them was non-trivial. The framework’s documentation and ecosystem fragmented for a period.

Next.js as of 2025 is on version 15+, with the App Router as the recommended approach. The framework is enormously capable and enormously opinionated. A team building on Next.js gets a deployment story, a routing story, a data-fetching story, a server-component story, and an authentication story — at the cost of accepting Vercel’s framing for how applications should be built. That’s a real trade. The chapter has to be honest about it.

Remix came from a different lineage.

Michael Jackson, Ryan Florence, and Kent C. Dodds were already well known in the React community as the authors of React Router, the de facto routing library for React applications since 2014. Their working position was that React Router had been solving the routing problem in client-side React for years, and that the broader application-architecture problem — data loading, mutations, error handling, progressive enhancement — was a natural extension of that work.

Remix launched in 2020, initially as a paid commercial product (with a “supporter license” model). The framework was open-sourced in October 2021 with the 1.0 release. The design centered on embracing the web. Forms submitted with real HTML form actions. URLs that worked when JavaScript was disabled. Data loading scoped to routes. Mutations through standard POST requests. Progressive enhancement as a first-class concern rather than an afterthought.

A Remix route, in the original API, looked something like this:

export async function loader({ params }) {
return getProductByID(params.id)
}
export async function action({ request, params }) {
const form = await request.formData()
return updateProduct(params.id, form)
}
export default function Product() {
const product = useLoaderData()
return (
<Form method="post">
<input name="name" defaultValue={product.name} />
<button type="submit">Save</button>
</Form>
)
}

The structure made data loading, mutations, and rendering colocated. The form worked without JavaScript (server-side action). It worked better with JavaScript (client-side optimistic updates and validation). The framework’s mental model was deliberately closer to Rails than to single-page application — server-side as the source of truth, client-side as a progressive enhancement.

Shopify acquired Remix in October 2022. The acquisition was strategic; Shopify wanted Remix as the foundation for Hydrogen, its commerce-focused storefront framework. The Remix team stayed on at Shopify; the framework continued open-source development; the commercial alignment moved from license fees to platform integration.

In 2024, the Remix team announced that Remix would merge into React Router 7. The conceptual model that Remix had been arguing for — server-rendered routes with loaders and actions, working forms, progressive enhancement — became the default for React Router itself. Existing Remix applications can continue running; new applications use React Router 7, which is effectively Remix in different packaging. The merge is one of the more graceful framework consolidations in recent memory.

The Jackson/Florence/Dodds team’s contribution to this era is hard to overstate. They consistently argued for the platform-first approach within the React community when the dominant patterns were pulling the other direction. Many of the meta-framework conventions we now take for granted — file-based routing, data loaders, form actions, error boundaries scoped to routes — were either originated or popularized through their work. The framework that ultimately won was largely the framework they had been building.

Astro takes a different angle on the meta-framework problem.

Fred K. Schott started Astro in 2021. The framework’s central design choice was to render everything to static HTML by default, and to opt in to client-side JavaScript only where necessary. A typical Astro page is a .astro file that looks like a hybrid of HTML, Markdown, and JSX:

---
const posts = await fetch('/api/posts').then(r => r.json())
---
<html>
<body>
<h1>Blog</h1>
{posts.map(post => (
<article>
<h2>{post.title}</h2>
<p>{post.summary}</p>
</article>
))}
<SearchBox client:load />
</body>
</html>

The content above the dashes runs at build time (or request time, for server-rendered Astro). The HTML below is produced server-side. The <SearchBox> component, marked with client:load, is the only thing that ships JavaScript to the browser. Everything else is static HTML.

This is the islands architecture — interactive components are islands in a sea of static content. The pattern was named by Etsy’s Katie Sylor-Miller and Preact’s Jason Miller in 2020; Astro is the most prominent framework built around it.

Astro’s design fits a particular category of product well. Marketing sites, documentation, blogs, content-heavy applications, and anything that mostly needs static HTML with a few interactive bits — these benefit substantially from shipping less JavaScript. The framework’s adoption has been strong in the content space; the Vercel-hosted documentation for many other frameworks (including, ironically, Next.js’s competitors) is often built on Astro.

Astro also accepts components from other frameworks. An Astro page can include a React component, a Vue component, a Svelte component, a Solid component, or a Lit element — each rendered server-side by default, with client-side hydration as an opt-in. The framework is, in this sense, framework-pluralist. It doesn’t pick a single client-side framework; it provides the meta-framework scaffolding and lets the team choose which component model to use for which interactive surface.

This book’s planning ships on Astro, incidentally — the Starlight documentation framework Astro built. The choice was deliberate.

The other major meta-frameworks are largely lineal descendants of the design ideas above, applied to other component frameworks.

Nuxt is the Vue meta-framework. The project was started in 2017 by Sebastien Chopin and his brother Alexandre, modeled deliberately on the early Next.js. Nuxt 3 (2022) modernized the framework around Vue 3’s Composition API and is currently on Nuxt 4. Like Next.js, Nuxt is institutionally aligned with a hosting company (NuxtLabs, which has commercial offerings around Nuxt deployment). The Vue ecosystem’s relative independence from a single corporate sponsor extends to Nuxt’s governance, though.

SvelteKit is the Svelte meta-framework. Rich Harris and the Svelte team started SvelteKit in 2020. Harris joined Vercel in 2021, and SvelteKit has been developed inside Vercel’s open-source organization ever since. The framework’s design echoes Next.js (file-based routing, server rendering, data loading) with Svelte’s compile-time approach to the component model. SvelteKit 1.0 shipped in late 2022; the framework has been stable and well-regarded ever since.

SolidStart is the Solid meta-framework. Ryan Carniato and the Solid team have been building it since 2022. The framework is younger than the others in this section, but the design has been thoughtful — SolidStart picked up the Remix-style loader/action pattern, the islands architecture, and the React Server Components-style server/client component split, while keeping Solid’s fine-grained reactivity model.

TanStack Start is the youngest of the major meta-frameworks. Tanner Linsley, who created the TanStack ecosystem (TanStack Query, TanStack Router, TanStack Table — libraries widely used inside React applications), released TanStack Start in 2024 as a meta-framework that integrates the existing TanStack libraries with server rendering and data fetching. Linsley’s work is one of the more interesting independent voices in the modern React ecosystem; TanStack is funded through GitHub Sponsors and consulting, and the libraries are aggressively framework-agnostic where possible.

The pattern across these meta-frameworks is consistent enough to be a working principle. Each major component framework (React, Vue, Svelte, Solid) has, by 2025, a primary meta-framework that bundles routing, data loading, server rendering, deployment, and a set of conventions. The meta-frameworks differ in details but share an architecture. They’re all, in a sense, Next.js applied to a different component framework.

React Server Components: The 2023 Inflection

Section titled “React Server Components: The 2023 Inflection”

The most consequential paradigm shift in the meta-framework era is React Server Components.

The technology was first announced by the React team in December 2020 (Lauren Tan and Dan Abramov’s Introducing Zero-Bundle-Size React Server Components talk and post). The implementation matured over the following years. Next.js’s App Router (2023) was the first major framework to ship a production-ready RSC implementation. The pattern has since spread to Remix/React Router 7 and is being adopted across the React ecosystem.

The technical idea is that some components run on the server only — they never ship to the client, they can access databases and file systems directly, and their rendered output is streamed to the client as part of the HTML response. Other components run on the client — they ship their JavaScript, they hydrate, they handle interactivity. The two kinds of components compose, with server components rendering client components inside themselves.

A simplified RSC example:

// app/products/page.js — server component
async function ProductsPage() {
const products = await db.products.findAll() // direct DB access
return (
<div>
<h1>Products</h1>
<ProductList products={products} />
<AddToCartButton /> {/* this is a client component */}
</div>
)
}
// AddToCartButton.js — explicitly a client component
'use client'
function AddToCartButton() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}

The 'use client' directive at the top of AddToCartButton.js marks it as a client component. Everything else defaults to running on the server.

The architectural argument is strong. The framework can ship significantly less JavaScript to the browser (only the components that need interactivity, plus the data they need). Database access happens on the server, where it belongs. The boundary between what runs where becomes explicit in the code. For applications with a lot of data-heavy pages and a small amount of interactivity, the model is a substantial win over SPA-style React.

The trade-offs are real. The mental model is more complex. The boundary between server and client is enforced at the file level and has subtle implications (server components can pass data to client components but not the other way; client components can’t render server components except through specific patterns; the network of imports has to respect the split). Tooling, debugging, and the developer-experience story are still maturing. Several other frameworks (including Vue and Svelte) are exploring similar patterns; the field hasn’t fully converged on what the right model is.

For this book, RSC is significant because it represents the framework reaching further into the server. The meta-framework was already responsible for server rendering. RSC makes the framework responsible for server-side application logic that was previously written separately. The frontend has expanded into territory that used to be the backend’s. The “full-stack” framing of modern frontend development is, in part, this expansion.

A thread runs through this chapter that’s worth naming directly.

Next.js → Vercel. SvelteKit → Vercel. Remix → Shopify (now part of React Router). Hydrogen → Shopify. The independent meta-frameworks (Astro, TanStack Start) are notable partly because they’re the exceptions.

The pattern is the same one Chapter 18 named in the desktop-software context. Open-source infrastructure is increasingly developed inside large companies whose commercial interests benefit from the infrastructure’s adoption. The relationship is mutually beneficial in the short term — the company gets a feature-rich product, the framework gets sustained engineering resources, the community gets free software — and structurally concentrated in the long term. The framework’s roadmap is the company’s roadmap. The framework’s strategy is the company’s strategy.

This isn’t a critique of any individual company. Vercel has been a responsible steward of Next.js and SvelteKit, by the standards of corporate open-source sponsorship. The work has been good. The community has been respected. The license terms remain open.

The observation is that the meta-framework you choose determines, in part, which hosting platform you’re aligned with. Next.js applications deploy most easily to Vercel. SvelteKit applications deploy most easily to Vercel. Remix applications (when it was independent) had more cross-platform deployment options; the merger into React Router has preserved some of that. The cost of switching meta-frameworks is non-trivial; the cost of switching hosting platforms inside a meta-framework is usually higher than it should be.

This connects to the supply-chain argument from Chapter 17. The platform — governed by W3C, WHATWG, and TC39 — is durable. The frameworks above the platform are not, and the companies above the frameworks are not. Code written against the platform travels. Code written against a particular framework’s specific server-component model is, in practice, harder to move.

Rendering Concerns vs. Capability Concerns

Section titled “Rendering Concerns vs. Capability Concerns”

This is the chapter’s architectural argument, and it’s worth landing carefully.

Meta-frameworks address rendering concerns well. How the UI is produced. Where it renders. How it hydrates. How it updates. How components compose. How URLs map to pages. How data flows from the server to the client. How forms submit. How errors are caught and shown.

Meta-frameworks address capability concerns less cleanly. Analytics. Audit logging. Notifications. Storage. Permissions. Validation. Feature flags. Observability. Design-system rules. Each of these capabilities still needs a home in the application; the meta-framework’s component model is the only home it provides; the result is that capabilities end up entangled with components in the same way that Chapter 13 described.

This distinction will matter throughout Parts II and III of this book. The Kitsune architecture introduced in Part III is, in part, a way to separate these concerns. Rendering (which the meta-framework or a component library can handle) versus capability composition (which Kitsune’s modules and boundaries handle). The two can coexist. A team can use Next.js for rendering and Kitsune for capabilities. The point is that they shouldn’t have to be the same system.

A meta-framework that owns everything is convenient. A meta-framework that owns rendering, with a separate capability system above it, is more flexible. The convenience is real. The flexibility matters more as applications age.

This chapter has been about the framework around the framework — the layer that grew up around component libraries to handle the application-level concerns components alone couldn’t solve. The next chapter steps underneath all of this to look at the JavaScript runtimes — Node, Deno, Bun — that the meta-frameworks themselves are built on. Chapter 17 looks at the dependency ecosystem those runtimes ship through. Chapter 18 looks at what happens when web technologies become the substrate for non-web application development. And Chapter 19 closes Part I with the hinge moment: the platform itself, while all of this abstraction was being built on top of it, was quietly catching up.

Pick a meta-framework you know — Next.js, Remix/React Router 7, SvelteKit, Nuxt, Astro, SolidStart, TanStack Start, or another.

Create a table of these responsibilities:

  • Routing
  • Layouts
  • Server rendering
  • Client rendering
  • Hydration
  • Data loading
  • Mutations
  • Forms
  • Caching
  • Error handling
  • Asset optimization
  • Deployment targets
  • Authentication
  • Observability
  • Styling

For each one:

  1. Does the framework own this concern, or delegate it?
  2. Does the browser provide native support for any part of it?
  3. Is this a rendering concern, a data concern, a deployment concern, or a capability concern?
  4. If the framework went away tomorrow, how much of your application’s code would survive? How much would need to be rewritten?
  5. Which of the framework’s choices are tied to a specific hosting platform?

The point isn’t to discredit meta-frameworks. The point is to see the meta-framework as what it is — a bundle of architectural decisions made by a specific team for a specific context, useful when its decisions align with your product’s needs and costly when they don’t.