SolidJS vs. Next.js: You're Comparing the Wrong Two Things
SolidStart is the real comparison. 24 of 92 component files here ship client JS — the number that decides fine-grained reactivity against RSC.
SolidJS and Next.js are not the same layer of the stack, which is the first thing to fix about this comparison. SolidJS is a UI library, the way React is. Its meta-framework is SolidStart. So the real question is SolidStart versus Next.js — and the axis that decides it is not rendering speed. It is how much component code reaches the browser at all.
The short answer
Solid makes updates cheap: fine-grained signals, no virtual DOM, and a compiler that wires each expression to the exact DOM node it owns. Next.js App Router makes components absent: React Server Components mean most of the tree never ships to the browser in any form. Solid optimises the runtime you ship; Next optimises whether you ship one.
What this storefront is, in numbers
We run Next.js 16.2.9 and React 19.2.4. We do not run Solid, so nothing here is a benchmark — there is no "we ported it and measured both," because we did not. What this repo can contribute is the shape of a real App Router codebase, which is the thing the comparison usually argues about in the abstract:
| Layer | Files | Carrying "use client" |
|---|---|---|
| Atoms | 7 | 0 |
| Molecules | 41 | 19 |
| Organisms | 33 | 5 |
| Templates | 11 | 0 |
| Total | 92 | 24 |
Plus 27 page.tsx routes, 2 layout.tsx files, and 8 server-action modules.
Sixty-eight of 92 component files ship no client JavaScript for their own logic. Not "less JavaScript" — none. They run on the server, emit HTML, and their component code stays out of the bundle entirely.
That number is the whole argument. In a Solid app, every component that renders is client-capable code by default; Solid's answer to bundle size is that its runtime and its compiled output are famously small. Next's answer is categorically different: two-thirds of these files have no browser-side existence to optimise.
The comparison that matters
| SolidStart (SolidJS) | Next.js App Router (React) | |
|---|---|---|
| Reactivity | Fine-grained signals; no virtual DOM | VDOM reconciliation, plus RSC for the static majority |
| Re-render unit | The expression bound to a DOM node | The component function |
| Server/client boundary | Per-file server functions, islands | "use client" through the module graph |
| Default for a plain component | Ships and runs in the browser | Server Component — ships no component JS |
| Compilation | JSX compiled to direct DOM operations | JSX to createElement; RSC serialised to the client |
| Ecosystem | Small, growing | Very large — the reason most teams land here |
| Hiring and answers | Specialist | Abundant |
The reactivity row is where Solid genuinely wins on merit. React re-runs a component function and diffs; Solid's compiler already knows that this text node depends on that signal, so an update is a targeted DOM write with no diffing and no component re-execution. For a dashboard with a thousand live cells, that is a real architectural advantage, not a micro-benchmark.
But look at what this storefront actually is: 27 mostly-static routes, a product catalog, a blog, and a handful of interactive islands. The expensive problem here was never "update this cell efficiently." It was "stop shipping code to pages that do not need it."
Where the boundary actually gets drawn
"use client" propagates through the module graph rather than being declared per file, which is the part people get wrong in both directions. A Server Component can render a Client Component; a Client Component cannot import a Server Component and have it stay on the server. So the boundary is a design decision about where interactivity begins, and in this codebase it lands almost entirely in one layer.
Nineteen of the 24 client files are molecules: the accordion, the gallery carousel, the edition picker, the download menu, the forms. Atoms are all server — a Button that renders a link needs no state. Templates are all server — they order organisms and hold no content. The interactivity concentrates exactly one layer down from where the sections live.
This is the discipline a fine-grained-reactivity framework does not impose on you, because it does not need to. In Solid, a static component is cheap; in React without RSC, a static component is still a component that ships. RSC is React's answer to a problem Solid solved from the other end.
Mutations: server functions on both sides
The two frameworks have converged more than the framing suggests. SolidStart has server functions; Next has Server Actions. Both let you write a function that only ever runs on the server and call it from a component as if it were local, and both exist so that a form submission does not require hand-writing an API route plus a fetch plus the types on both ends.
This repo has 8 server-action modules covering auth, redemption, account changes and the newsletter. The convergence is real, and the remaining differences are about defaults rather than capability:
- Next's actions are tied to the
"use server"directive and integrate with form submission and revalidation. - SolidStart's server functions sit closer to its router's own data primitives.
If mutation ergonomics are what you are choosing on, this is close to a wash. It is the read path — whether the component rendering the data exists in the browser at all — where the two models still diverge sharply.
Rendering: 27 routes that are mostly static
The other thing this codebase is, that a reactivity comparison tends to ignore: 27 routes, and the great majority of them are statically generated at build time. Product pages, the blog, the pricing page, the docs, the category hubs — all HTML on disk, served from a CDN.
For those pages, the fine-grained-versus-VDOM argument is nearly moot. Nothing is updating. The question is how many bytes of framework and component code the visitor downloads to look at a page that will never change, and the answer this stack gives is: for 68 of 92 component files, zero.
Solid's rejoinder is legitimate — its runtime is small enough that the comparison is not as lopsided as the file count implies, and SolidStart does static rendering too. The honest summary is that both frameworks arrive at "ship little JavaScript for static content" from opposite directions, and that a content-heavy site can be built well on either. What tips it is everything in the ecosystem row of that table.
The cost the model does not remove
One caution against reading the 68-of-92 number as a free win: RSC keeps component code off the client, but a client island can still drag a large dependency in behind it. This repo shipped exactly that bug. The Supabase auth SDK — about 68 KiB over the wire, 255 KiB parsed — was reaching every page on the site, because Header renders site-wide and imported the client at module scope.
// src/lib/supabase/lazyClient.ts — the fix
export function hasAuthCookie(): boolean {
if (typeof document === "undefined") return false;
return /(?:^|;\s*)sb-.+?-auth-token/.test(document.cookie);
}
export async function loadSupabaseClient(): Promise<BrowserClient> {
const { createClient } = await import("@/lib/supabase/client");
return createClient();
}
Deferring the import to the effect that uses it took the auth stack off the critical path of /, /blog, /docs and /pricing — pages with no account UI at all.
That failure mode is framework-independent. SolidStart would have had the identical problem with the identical fix, and it is worth saying plainly because "RSC means less JavaScript" is true of your components and says nothing about your dependencies.
Choosing
Choose SolidStart when update granularity is the hard problem — dense real-time UIs, editors, visualisation surfaces with thousands of independently-changing values — and your team is comfortable being early to an ecosystem.
Choose Next.js when most of your surface is content that could be static, when you need the ecosystem (auth SDKs, payment SDKs, MDX pipelines, image tooling) to already exist, or when the people maintaining it after you need to be findable. For a storefront of 27 routes and 111 product pages, that was not a close call.
Neither decision is about which renders a benchmark faster.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Comparing "SolidJS vs Next.js" and getting nowhere | Comparing a UI library to a meta-framework | Compare SolidStart to Next.js, or Solid to React |
"use client" spreading through the whole tree | A high-level component was marked client | Push the directive down to the leaf that owns the state |
| Bundle large despite mostly Server Components | A client island imports a heavy dependency at module scope | Dynamic-import it inside the effect that uses it |
| Expecting Solid's reactivity to fix load time | Fine-grained updates address update cost, not payload | Measure which one is actually your problem |
| React hooks habits failing in Solid | Solid components run once; signals are functions | Read the value by calling it — count(), not count |
Frequently asked questions
Did you benchmark Solid against Next.js?
No. We run Next and have never rebuilt this stack on SolidStart, so this post makes no performance claim about either. What it measures is our own client/server split — 24 of 92 component files carrying "use client" — which is the thing the two models genuinely disagree about.
Is Solid faster than React? On update-heavy workloads, its architecture is meaningfully better suited: no diffing, no component re-execution, DOM writes bound at compile time. Whether that is your bottleneck is the question worth answering before switching frameworks, and for a content-heavy site it usually is not.
Can I get fine-grained reactivity in Next.js? Not natively — React's unit of re-render is the component. What App Router offers instead is not shipping the component at all, which for static content is a stronger result than re-rendering it efficiently.
How does this compare to the other Next.js alternatives? The same axis runs through each of them: Next.js vs. Svelte and Next.js vs. Vue are the closest neighbours, and Server Components vs. Client Components covers the boundary rules this post leans on.
Templates in this post
ASoc Quill, ASoc Rally and ASoc Rank are Next.js + Tailwind landing page templates built with the same server-first split described above — static sections as Server Components, interactivity isolated to the molecules that need it.
Browse the full sets: Next.js landing page templates, Tailwind landing page templates.
