Over the years, I've developed a deep appreciation for Gatsby.js. Its ease of use, robust plugin ecosystem, and out-of-the-box solutions have made it a dependable choice for web development. However, the web development landscape is constantly evolving, and so are the tools we rely on. In this post, I’ll share my journey transitioning from Gatsby to Next.js, the challenges I faced, and why this move makes sense for developers considering a change.
When I first encountered Gatsby, it was a game-changer for the legacy tech I inherited at Spectrum Health. Back then, we were juggling Sitecore MVC in ASP.NET and Angular. The majority of our content was static, yet the tech stack relied heavily on inefficient full-page renders. This was a clear mismatch for our needs.
Our goal was to modernize the stack with a solution that supported both Static Site Generation (SSG) and Server-Side Rendering (SSR). Early evaluations of Gatsby and Next.js revealed two distinct approaches:
Ultimately, Gatsby’s comprehensive ecosystem won us over. Features like pre-fetching, optimized image handling, and a tightly integrated GraphQL layer helped us streamline web performance, aligning perfectly with our shift to cloud-native, continuous delivery practices.
When I transitioned to TestifySec, I brought my love for Gatsby with me. It remained the backbone of our UI development, delivering the simplicity and performance we valued.
However, following Netlify's acquisition of Gatsby, it became apparent that Gatsby’s trajectory was shifting. A year after the acquisition, updates slowed to maintenance mode. The most recent release, Gatsby 5.14, prioritizes bug fixes and security patches, with little innovation or new features.
This stagnation prompted me to reevaluate my reliance on Gatsby. It’s still a fantastic tool, but the lack of forward momentum suggests it may not be the best choice for future-proofing projects.
I began by exploring Svelte, a framework gaining traction for its simplicity. Svelte feels closer to traditional HTML development, focusing on minimalism and performance. However, its verbosity and relative immaturity didn’t align with my needs for a modern, scalable application.
I also considered Remix, a framework built on React that promises a more robust developer experience. While intriguing, Remix still felt immature and not as widely used as Next.js. This may change someday and is worth keeping an eye on.
Vue and Nuxt were also on my radar, but my familiarity with React and the broader React ecosystem made Next.js a more natural choice.
Next.js, on the other hand, offered an enticing blend of familiarity and innovation. Using the create-next-app
starter, I was up and running with TailwindCSS in minutes. Its fast build times and clean developer experience immediately impressed me.
What stood out most was how closely Next.js mirrors Gatsby in key areas:
<Image>
component for responsive, optimized images, akin to Gatsby’s.<Link>
component in Next.js mirrors Gatsby’s approach, enabling efficient client-side routing and prefetching.These similarities eased the transition, allowing me to focus on building rather than relearning fundamentals.
One area where Gatsby still holds the upper hand is its “stupid-simple” approach to integrating MDX. With Gatsby, setting up MDX is as easy as installing a plugin and dropping files into the appropriate folder. In contrast, Next.js requires a bit more effort:
@next/mdx
, @mdx-js/loader
, etc.).next.config.js
file.##[slug].tsx
## an example of how to handle MDX in Next.js
export async function getStaticPaths() {
const posts = fs
.readdirSync(BLOG_DIR, { withFileTypes: true })
.flatMap((file) => (file.isDirectory() ? fs.readdirSync(path.join(BLOG_DIR, file.name)) : file.name))
.filter((file) => file.endsWith('.mdx'))
.map((file) => ({ params: { slug: file.replace(/\.mdx$/, '') } }));
return {
paths: posts,
fallback: false,
};
}
export async function getStaticProps({ params }: { params: { slug: string } }) {
const filePath = path.join(BLOG_DIR, `${params.slug}.mdx`);
const fileContent = fs.readFileSync(filePath, 'utf-8');
const { data, content } = matter(fileContent);
const mdxSource = await serialize(content, { parseFrontmatter: true });
return {
props: {
metadata: data,
mdxSource,
},
};
}
While straightforward, this process highlights a key difference between the two frameworks. Gatsby prioritizes simplicity through extensive abstractions, whereas Next.js offers flexibility, requiring developers to handle more themselves.
For more information on the challenges with MDX in Next.js, and how I worked through them, read my article Challenges with MDX and Next.js
Building my portfolio website with Next.js provided valuable insights:
For teams considering a migration, here’s a practical approach:
create-next-app
to generate a new Next.js project.<Image>
and <Link>
.gray-matter
or next-mdx-remote
for parsing and rendering content.While this process may involve some upfront effort, the long-term benefits of a modern, actively developed framework like Next.js are well worth it.
me:
Gatsby remains a remarkable tool, especially for teams seeking a low-maintenance, high-productivity framework. However, with Next.js leading the pack in innovation, popularity, and community support, now might be the time to consider a switch.
For developers already familiar with Gatsby, the transition is smoother than you might think. Next.js retains much of what made Gatsby great while offering a more flexible foundation for future growth.
If I do tackle a full migration in the future, I’ll be sure to document the process and share performance benchmarks. For now, I encourage you to explore Next.js and see if it’s the right fit for your next project.
2024-11-27