It’s not just you, Next.js is getting harder to use
Andrew Isreal:
I wrote a blog post the other day about how Next.js Middleware can be useful for working around some of the restrictions imposed by server components. This led to some fun discussions in the world about whether this was a reasonable approach or if Next.js DX was just... bad.
From my perspective, Next.js’ App Router has two major problems that make it difficult to adopt:
- You need to understand a lot about the internals to do seemingly basic tasks.
- There are many ways to shoot yourself in the foot that are opt-out instead of opt-in.
To understand this better, let’s look at its predecessor, the Pages Router.
A quick look at the Pages Router
When I first learned about Next.js, the main “competitor” was Create React App (CRA). I was using CRA for all my projects, but I switched to Next.js for two reasons:
- I liked file-based routing because it allowed me to write less boilerplate code.
- Whenever I ran the dev server, CRA would open http://localhost:3000 (which gets annoying fast), and Next.js didn’t.
The second one is maybe a little silly, but to me, Next.js was:
React with better defaults.
And that’s all I really wanted. It wasn’t until later that I discovered the other features Next.js had. API routes were exciting as they gave me a serverless function without setting up any extra infra - super handy for things like “Contact Us” forms on a marketing site.
getServerSideProps
allowed me to run basic functions on the server before the page loaded.Those concepts were powerful, but they were also simple.
An API route looked and acted a lot like every other route handler. If you had used Express or Cloudflare Workers, you can squint at a route handler and all the concepts you already knew translated.
getServerSideProps
was a little different, but once you understood how to get arequest
and the format of the response, it turned out to be pretty straightforward too.The App Router release
The Next 13 release introduced the App Router, adding many new features. You had Server Components which allowed you to render your React components on the server and reduce the amount of data you needed to send to your client.
You had Layouts, which allowed you to define aspects of your UI shared by multiple routes and didn’t need to be re-rendered on every navigation.
Caching got… more sophisticated.
And while these features were interesting, the biggest loss was simplicity.
[...]
You are no longer yelling, “Why does this not work?” but rather, “Why does this work… like that?”
The App Router, unfortunately, is full of these kinds of subtleties.
Just because something is recommended, doesn’t mean it’s right for you
Next.js has officially recommended that you use the App Router since before it was honestly ready for production use. Next.js doesn’t have a recommendation on whether TypeScript, ESLint, or Tailwind are right for your project (despite providing defaults of Yes on TS/ESLint, No to Tailwind - sorry Tailwind fans), but absolutely believes you should be using the App Router.
The official React docs don’t share the same sentiment. They currently recommend the Pages Router and describe the App Router as a “Bleeding-edge React Framework.”
When you look at the App Router through that lens, it makes way more sense. Instead of thinking of it as the recommended default for React, you can think of it more like a beta release. The experience is more complicated and some things that were easy are now hard/impossible, but what else would you expect from something that’s still “Bleeding-edge?”
So when you are picking a framework for your next project, it’s worth recognizing that there are still many rough edges in the App Router. You might have better luck reaching for a different tool that’s more suited to your use case.
Andrew makes many excellent points in his post, I preferred next's page router over the app router, and the DX has just been getting worse.
These days, I'm usually working in Astro or Remix where both have a better DX experience.