Gatsby vs Qwik: One Moves the Data, the Other Moves the Hydration
Gatsby resolves data at build time; Qwik never hydrates. Measured against a build that chose neither: 846 static pages, no GraphQL, 24 of 92 components on the client.
Gatsby and Qwik both attack the cost of JavaScript, from opposite ends. Gatsby resolves data at build time behind a GraphQL layer and ships a hydrating React bundle. Qwik ships almost no JavaScript at all, resuming serialized state when the user first interacts. Picking between them is really picking which half of your page is expensive.
This is not a benchmark post. It is a comparison written from a production Next.js storefront that chose neither, where both of the problems these frameworks solve are visible and measurable — 846 prerendered pages, no GraphQL layer, and 24 of 92 component files carrying "use client".
The two frameworks solve different problems
| Gatsby | Qwik (with Qwik City) | |
|---|---|---|
| Core idea | Pull all data into a GraphQL layer at build time | Resumability — never hydrate, resume from serialized state |
| What it optimises | Where data is resolved | When JavaScript executes |
| Rendering | SSG first, SSR added later | SSG/SSR, with lazy code loading per interaction |
| Component model | React | Qwik's own JSX with $ boundaries |
| Data access | useStaticQuery / page queries against GraphQL | routeLoader$, plain fetch in a loader |
| JS on a static page | React runtime + page bundle, hydrated | Near zero until interaction |
| Ecosystem | Large plugin set, React libraries work | Small; React libraries need qwik-react |
| Status note | Acquired by Netlify in Feb 2023; Gatsby Cloud shut down Aug 2023 | Younger, far smaller adoption than React |
Read that table as two different bets. Gatsby bets that your pain is content plumbing — many sources, many shapes, and a build that needs to normalise them. Qwik bets that your pain is the hydration tax — a mostly-static page that still downloads and re-executes a component tree to attach a handful of event handlers.
If you know which of those two sentences describes your site, you have your answer, and the rest of this post is about how to find out.
Gatsby's bet: the data layer is the hard part
Gatsby's distinguishing machinery is the GraphQL layer. Source plugins pull from a CMS, the filesystem, an API; everything lands in one schema; pages query it at build time, and the result is baked into static HTML.
When you have six content sources and a team of editors, that normalisation is real work done for you. When you have one source, it is a layer of indirection with its own query language.
That is the case here. The catalog is a TypeScript module — 8,114 lines, 111 products — and the route that renders every product page imports it directly:
// src/app/templates/[slug]/page.tsx
import { catalog, getProduct, type TemplateProduct } from "@/data/catalog";
export function generateStaticParams(): Params[] {
return catalog.map((p) => ({ slug: p.slug }));
}
There is no graphql dependency in this project at all. The 349 blog posts are MDX compiled at build time against a typed registry, which is the same story in a second content type. A build produces 846 prerendered pages:
✓ Compiled successfully in 19.8s
✓ Generating static pages using 3 workers (846/846) in 14.5s
Gatsby would produce the same pages. The difference is that every component that wanted a product would ask GraphQL for it instead of importing a typed array, and the type safety would come from generated types rather than the declaration you already wrote. React vs Gatsby takes that trade apart properly — the short version is that the GraphQL layer is Gatsby's main feature, so it is also its main cost when you do not need it.
Qwik's bet: hydration is the hard part
Qwik's claim is narrower and more interesting: the problem is not how much JavaScript you ship, it is that a server-rendered page re-runs its whole component tree in the browser to become interactive. Qwik serializes the state and the listener locations into the HTML, so the page is interactive immediately and code arrives per interaction.
The honest question is how much that is worth on your page, which means counting what actually ships. For this site:
| Measure | Count |
|---|---|
Component files under src/components | 92 |
Files carrying "use client" | 24 |
| Organisms on the home page | 10 |
| Of those, Client Components | 1 (Header) |
| Client molecules rendered inside server sections on the home page | 3 (TemplateCard, UseCaseCard, NewsletterForm) |
So the home page's interactivity is a mobile menu, three card behaviours and a newsletter form. Everything else — Hero, TechStack, Features, FeatureTabs, Testimonials, Blog, Footer and the molecules inside them — renders on the server and ships no component JavaScript at all.
That is React Server Components doing a large part of what Qwik's resumability does, by a different route: Qwik avoids hydrating the component, RSC avoids sending it. The end state on a marketing page is close enough that the remaining difference is the hydration cost of those four client components, and desktop Lighthouse here is already 100 for performance across all eight main page types (mobile 89–99). There is not much left on the table for resumability to collect. Qwik vs Next.js measures that gap directly.
Where Qwik's model would actually pay here
The marketing pages are the wrong place to look, and this is the part most comparisons miss. The interactive weight of this app is not on the pages with traffic — it is behind the login.
Six of the 24 client components are reachable only with a session: AuthCard (the four auth routes), SettingsForm, RefundButton, RedemptionPicker, DashboardTabs and YourProductsGrid. Those are the routes that render ƒ in the build output, per request, with state that matters. A dashboard with tabs, forms and per-row actions is exactly the shape where "download the code for the interaction the user performed, not the whole tree" is a real win.
And it is exactly the shape where Gatsby has nothing to offer, because none of it is build-time content.
So the useful framing for a mixed app is not "which framework" but which half of the app you are optimising:
| Your page is… | Gatsby helps | Qwik helps |
|---|---|---|
| Content from several CMSs, little interaction | Yes — this is its case | Marginally; there is little to hydrate |
| Single typed content source, little interaction | No — the layer is overhead | Marginally, same reason |
| Heavy interaction, data per user | No — not a build-time problem | Yes — this is its case |
| Large catalog with filters and modals | Partly (the catalog) | Partly (the filters) |
Most real sites are two rows at once, which is why neither framework wins outright and why a framework whose server/client boundary is per component tends to fit better than one that makes the choice globally.
What a migration actually costs
| To Gatsby | To Qwik | |
|---|---|---|
| Component rewrite | Low — it is React | High — Qwik's JSX, $ boundaries, serialization rules |
| Data layer rewrite | High — typed imports become GraphQL queries | Low — loaders take plain fetches |
| Third-party React libraries | Work | Need qwik-react, and may not serialize |
| Routing | File-based, familiar | File-based, familiar |
| Hiring and answers | Large React pool; Gatsby itself is in maintenance | Small pool, fast-moving |
The asymmetry is the point. Gatsby is a cheap move and an expensive commitment: the components port, the data layer does not, and the project's direction after the Netlify acquisition and the Gatsby Cloud shutdown is a real risk to weigh. Qwik is an expensive move with a genuine technical payoff — you are rewriting components into a different reactivity model, and you should only accept that bill if the hydration tax is measurably hurting you.
Mistakes to avoid when comparing them
| Mistake | Why it misleads |
|---|---|
| Comparing on "JS shipped" alone | Gatsby's number is a hydration cost; a server-rendered page without client components has no such cost to beat |
| Benchmarking a demo page | A page with no interaction flatters Qwik and tells you nothing about your dashboard |
| Assuming Gatsby means fast | Build-time data says nothing about runtime bundle size |
| Treating GraphQL as free type safety | Generated types still need a build step, and the declaration you wrote was already typed |
| Ignoring project health | Framework choice is a multi-year bet; maintenance status belongs in the comparison |
| Picking globally for a mixed app | Marketing pages and an authenticated dashboard want different things |
Frequently asked questions
Is Qwik faster than Gatsby? On a page with real interactivity, Qwik's model avoids work Gatsby's hydration performs, so it should be — but "faster" depends on what your page does. A Gatsby page with no client-side interaction and a Qwik page with none both end up fast, and neither number predicts your app's.
Can I use React components in Qwik?
Through qwik-react, with limits: anything relying on React hydration semantics or non-serializable state is where it gets awkward. Budget for it rather than assuming a drop-in.
Is Gatsby dead? Not dead, but it is no longer where the momentum is. Netlify acquired it in February 2023 and shut Gatsby Cloud down that August. Existing sites keep working; starting a new one in 2026 is a choice that needs a reason.
What should I use instead of both? If the question is a content site with some interactive pieces, a framework with a per-component server/client boundary covers both bets without choosing. That is what this codebase does: 846 static pages, 24 client components, and zero ISR because the data is compiled in. Server Components vs Client Components is the mechanism.
Templates that ship the server-first split
If you want the shape measured above — static pages, a handful of client components, no data layer to configure — these landing sets are built that way: ASoc Folio, ASoc Forge and ASoc Frame.
Browse the sets: Next.js landing page templates and Tailwind landing page templates. For the styling half of a Gatsby decision, see Gatsby vs Bootstrap.
