Skip to main content
ASoc
Comparison

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.

The ASoc Team9 min read

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

GatsbyQwik (with Qwik City)
Core ideaPull all data into a GraphQL layer at build timeResumability — never hydrate, resume from serialized state
What it optimisesWhere data is resolvedWhen JavaScript executes
RenderingSSG first, SSR added laterSSG/SSR, with lazy code loading per interaction
Component modelReactQwik's own JSX with $ boundaries
Data accessuseStaticQuery / page queries against GraphQLrouteLoader$, plain fetch in a loader
JS on a static pageReact runtime + page bundle, hydratedNear zero until interaction
EcosystemLarge plugin set, React libraries workSmall; React libraries need qwik-react
Status noteAcquired by Netlify in Feb 2023; Gatsby Cloud shut down Aug 2023Younger, 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:

MeasureCount
Component files under src/components92
Files carrying "use client"24
Organisms on the home page10
Of those, Client Components1 (Header)
Client molecules rendered inside server sections on the home page3 (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 helpsQwik helps
Content from several CMSs, little interactionYes — this is its caseMarginally; there is little to hydrate
Single typed content source, little interactionNo — the layer is overheadMarginally, same reason
Heavy interaction, data per userNo — not a build-time problemYes — this is its case
Large catalog with filters and modalsPartly (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 GatsbyTo Qwik
Component rewriteLow — it is ReactHigh — Qwik's JSX, $ boundaries, serialization rules
Data layer rewriteHigh — typed imports become GraphQL queriesLow — loaders take plain fetches
Third-party React librariesWorkNeed qwik-react, and may not serialize
RoutingFile-based, familiarFile-based, familiar
Hiring and answersLarge React pool; Gatsby itself is in maintenanceSmall 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

MistakeWhy it misleads
Comparing on "JS shipped" aloneGatsby's number is a hydration cost; a server-rendered page without client components has no such cost to beat
Benchmarking a demo pageA page with no interaction flatters Qwik and tells you nothing about your dashboard
Assuming Gatsby means fastBuild-time data says nothing about runtime bundle size
Treating GraphQL as free type safetyGenerated types still need a build step, and the declaration you wrote was already typed
Ignoring project healthFramework choice is a multi-year bet; maintenance status belongs in the comparison
Picking globally for a mixed appMarketing 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.

Keep reading

Comparison11 min read

Gumroad Alternative: The Six Jobs You Take Back In-House

A hosted storefront does six jobs. Another one changes the fee; owning your storefront takes them back as code — 1,607 lines here, file by file.

Read more