I built my first portfolio site with Gatsby in 2019 and shipped a bunch of client work on it through 2020-2021. It was the obvious choice at the time — React, GraphQL data layer, plugin ecosystem, fast static output. By early 2023 I’d had enough.

This is a short reflection on why I migrated to Next.js, what I lost, and what I gained.

What was good about Gatsby

  • GraphQL data layer. Once you got past the learning curve, querying images, markdown frontmatter, and remote APIs through a single GraphQL schema was elegant.
  • gatsby-image / gatsby-plugin-image. Best-in-class image handling for the era. Lazy loading, blur-up placeholders, responsive srcsets — all by default.
  • Plugin ecosystem. gatsby-source-wordpress, gatsby-source-contentful, gatsby-transformer-remark — for content sites, the plugins did 80% of the work.

What stopped working

A few things piled up:

  • Build times. A 200-page site that took 30 seconds in 2020 was taking 5+ minutes by 2022. Each plugin added overhead. Each MDX file was a parse hop. Editing a typo and waiting two minutes for the dev preview to rebuild was death.
  • Plugin churn. Plugins fell behind core upgrades; I’d hit version mismatches three times a year. By Gatsby v4/v5, several of my plugins had stopped working entirely.
  • Image plugin breakage. gatsby-plugin-image shipped a major rewrite. My old Img and GatsbyImage calls all needed migration. Same week, the plugin started erroring on certain SVGs.
  • The DSG / SSR confusion. Gatsby tried to add server-rendered modes (DSG, SSR) to compete with Next.js. The configuration surface exploded; the docs lagged. I spent a Saturday trying to understand what flags.PARTIAL_HYDRATION did and emerged with no useful answer.
  • Release cadence. Next.js was shipping every few weeks; Gatsby felt like it was holding ground. Vercel’s funding asymmetry against Gatsby Inc. became impossible to ignore.

What Next.js offered (in 2023)

Next.js 12-13 was where I made the jump. The Pages Router was still the recommended path; the App Router had just landed in beta and was clearly not ready.

What pulled me over:

  • getStaticProps / getStaticPaths were simpler than Gatsby’s GraphQL layer for static sites. Yes, you lose the unified schema. But for most projects, you don’t need a schema — you need a function that returns props.
  • next/image. Caught up to gatsby-plugin-image. Easier API for most cases.
  • Build times. ~3× faster on my biggest site. Fewer plugin layers.
  • Vercel deploy. One git push, one preview URL, one production URL. Not a Gatsby fault per se — just much more polished.

What I lost

  • The GraphQL data layer. For a site that aggregated WordPress + Contentful + local markdown, I had to write three separate fetch helpers. Manageable but uglier.
  • Some image polish. gatsby-plugin-image’s art-direction support was better than next/image’s. Most sites don’t need it.
  • Confidence in image processing. next/image’s on-demand transform pipeline broke a few times in early Next.js 13.

What I’d tell myself in 2023

  • The migration takes longer than you think. Budget two weekends, not one.
  • Don’t try to keep Gatsby’s GraphQL schema mental model; rewrite the data flow with getStaticProps from scratch.
  • Don’t migrate during a Next.js major version transition. I started during Next 12 and was on Next 13 by the time I finished. Things broke in interesting ways.
  • The single biggest win wasn’t features — it was maintenance burden. A simpler stack with one big sponsor beat a more elegant stack with a fading one.

(Three years later, I’ve left Next.js too — but that’s a separate post.)

← All articles