Next.js first impressions

I started a new web app recently and was surprised to see the official React documentation pushes users to Next.js when starting a new project. This is crazy! React is actively moving potential users away from using it directly and onto an abstraction layer on top of it. The world’s most popular framework is willingly giving up that title.

As a disclaimer, I spent the past few years working on a React app but not as a frontend developer. Now that I am out of that role, it is surprising how much Next.js has moved into the mainstream. I am writing this from the perspective of someone with a basic background attempting to get something deployed in a few hours, not as an expert.

I gave it a try and these are my first impressions.

The good

Faster development. It is easy to get started and get a working full stack app in Next.js quickly! I was able to go from blank slate to an app deployed on a custom domain in a few hours. Next.js is going to make programming more accessible for the next generation of programmers. Especially with ChatGPT to help code and Vercel to handle deployments, web application development will be more accessible than ever before.

Next.js is a nuts-and-bolts-included framework. For example, they extended <img /> into <Image />, which you can point to a local image on your development file system. Next.js will create multiple sizes of the image, serve the right size based on the user device, only load it when the element is in the user viewport and (if used with Vercel) upload and serve the image over a CDN. This is a quality of life improvement for web development that blows my mind. Including this level of detail of best practices will make web apps better for everyone.

Vercel is great to use: simple, straightforward UX. Easy to plug in a Github repo, get it deployed on a custom domain and roll out new updates with a git push.

The bad

File-system routing is awkward. With the app based routing, all components get the file name page.js. That seems like an unfortunate choice when a lot of IDE UX is built around showing and searching file names. Dynamic routing also took me a while to understand. A username page at example.com/u/<username> would be in src/app/u/[username]/page.js. You can then load the username slug as a parameter passed into the component. The use of square brackets is a small but irritating frustration since they need to be escaped in shells. My preference would be having more of the routing in code, as component decorators for example.

Data fetching and caching are hard to understand. You need to take the time to understand the native Next.js methods, which have a non-elegant design. See this Reddit thread and the linked Gitbub discussion as an example of user misunderstanding and frustration. It is not obvious to me why router.refresh() is the name of an API call for a data update after a mutation.

Data fetching and caching is built on top of HTML web forms, and you need to spend time to relearn it if you did data fetching another way in React. Similar to the linked discussion, I gave up and ended up doing client side data rendering after spending some time trying to follow the Next.js documentation.

Mental overhead of server-side rendering. In a maybe too ideal world, you can just write your code, not think about where it runs, and the app “just works.” The framework would decide what runs where and when.

In practice, you need to keep on which components are rendered at build time, at serve time (server side) and on the client. setSate and connecting to your database will force setting components as server or client side, sometimes arbitrarily. A related frustration is that “use client”’s behavior is not actually client side rendering; they are pre-rendered in the server then hydrated in the client. The hydration is not intuitive. This all adds a small mental overhead to development.

The app router migration seems like an upcoming disaster. Next.js is moving from a “page based router” to a new “app based router” with a vague promise to keep supporting both and no timeline for deprecation of the page based router. Most third party documentation (and knowledge in ChatGPT) uses the pages based router without mentioning it. This will likely end up being a painful py2to3 situation, but with a much less mature platform and community.

Deployments tied to Vercel. I did not attempt to deploy to a non-Vercel environment. For mainstream enterprise adoption, third party clouds (AWS, Azure, GCP) will need to have first class support. The incentives for Vercel to improve this are poor; they make money based on the delta of the experience deploying on Vercel compared to other environments.

What’s next

It is clear that Next.js represents the future of web development: a nuts-and-bolts-included, opinionated framework more powerful than React with the performance of static websites. The future will be automatic server-side rendering, alongside automatic isolation and scaling of services.

This feels like the early days of Kubernetes for cloud orchestration frameworks. Next.js is one of the first to define the space and has been adopted quickly, but it will not necessarily be the solution the industry converges to. Poor developer experience of Next.js is already becoming a meme. First class deployment to all clouds will be a non-starter.

A new framework may emerge iterating on the best ideas from Next.js. We are still early.