The dark side of Next.js
Tags: webdev, javascript, nextjs, react
Published: 2023-02-19 14:49:54

_Header image by [Max Bender](https://unsplash.com/de/@maxwbender?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/photos/XIVDN9cxOVc?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)_

## Introduction
Next.js is one of the most popular frontend (meta) frameworks in 2023 and for good reason. However it has a dark side that nobody talks about, yet it can bite you in the back when you try to scale a Next based project. Let me show you the worst parts of Next.js so you can prepare for them in advance. To be upfront, most of these should be rare cases.

## Scaling concerns

If you host Next.js yourself and do multi-instance scaling, ISR won't be your best friend. Each instance will have its own cache for the generated pages. If your load-balancer is not using [sticky sessions](https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/), that means trouble. Depending on the status of the regeneration process on each node, the visitor can be served both stale and up-to-date content during the same sessions. That's bad, like really bad from the user experience point of view. _Here's a flow diagram to demonstrate it:_

[![flow diagram of ISR rendering messing up in multi instance next.js projects](https://s6.imgcdn.dev/SmXsd.png)](https://imgcdn.dev/i/SmXsd)

The [on-demand ISR](https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration#on-demand-revalidation) can help in minimizing this risk, but can't fully eliminate it while also complicating both your codebase and your operational processes quite a lot. That's far from ideal.

There are four solutions to this problem. 

1. First is to avoid it completely by replacing ISR with SSR. Not a what you would prefer, right? 
2. Second is using [Vercel](https://www.vercel.com/) to host your project or any similar platforms and let them scale your application. Not an option either? 
3. Then enter [Next Boost](https://github.com/next-boost/next-boost) a community library to boost your backend rendering performance by orders of magnitude.
4. Set up additional infrastructure around your Next.js instances. (_more on this later_)

Next Boost works as a cache in front of SSR-ed pages, like a custom implementation of ISR but here you can configure where to store the results and their [redis-cache](https://github.com/next-boost/redis-cache) option is ideal to be used with a cluster of Next.js servers. It will be a single-source-of-pages for all the nodes.

Sadly, this solution is not without downsides either. It complicates your tech stack and adds a new operational and maintenance cost factor. More importantly, the maintainer doesn't seem to be very active, so you might need to fork and customize the package if its current capabilities doesn't suit your project.

#### Back to Option 4:
> The [Reddit community](https://www.reddit.com/r/reactjs/comments/117pc07/the_dark_side_of_nextjs_and_how_to_tackle_it/) was kind enough to raise my attention to these. Thanks everyone and especially for [beny27](https://www.reddit.com/user/beny27/), [dbbk](https://www.reddit.com/user/dbbk/) and [porcupineapplepieces](https://www.reddit.com/user/porcupineapplepieces/)!

You can implement a similar solution to Next Boost on your own. The main idea is to add some kind of caching in front of your Next servers like an AWS S3 bucket and configure a load balancer to look up the responses there first or by setting up a CDN. Both of these examples need ISR switched off and using SSR instead.  

⬆️⬆️⬆️ **_If you need to scale custom Next.js servers these are the things you should keep in mind._**

## Architectural concerns
If you work on a large-scale project, chances are you run multiple applications from the same top-level domain and you need to support i18n. Well, that's a hard situation for Next.js!

The culprit is the router, Dhanraj Padmashali [@psygik](https://twitter.com/psygik) wrote about it in detail [here](https://dhanrajsp.me/blog/the-troubles-with-nextjs#_next-and-cdn). To sum it up, it's currently ~~not possible~~ hacky to run multiple Next.js apps under the same domain if their url paths contain a language code prefix. For example: `site.com/en/app` or in general: `site.com/{:langCode}/{:subApp}`.

The community created some workarounds to escape this limitation, but you need to be mindful to either [monkey patch](https://github.com/vercel/next.js/discussions/25681#discussioncomment-2026813) the `next/router` or add an [app wide middleware](https://github.com/vercel/next.js/discussions/25883#discussioncomment-2155780) to rewrite the URL or use even [more drastic measures](https://github.com/vercel/next.js/discussions/17078#discussioncomment-387358).

Dhanraj also points out that if your whole application lives under a path of a larger site, e.g.: `site.com/cool-apps/next-app` that gets internally proxied to another URL and the app uses dynamic route parameters for SSR'd pages eg: `/next-app/users/beastmaster64` it will die on the internal `_next/data` requests for the `json ` page data because that folder doesn't respect the `assetPrefix` nor the `basePath` values. Yes, complicated, but not uncommon. There's a [workaround](https://dhanrajsp.me/blog/the-troubles-with-nextjs#_next-and-cdn) in the linked article, but it's again, rather hacky and needs a custom server.

⬆️⬆️⬆️ **_Keep these in mind while planning your architecture._**

## Router limitations 
This is a technical detail which can only become a show-stopper in very few cases, nonetheless, it might affect you. The built-in, file-based router cannot provide "real" client-side dynamic navigation. If your application **really** needs that, then take a look at [this guide](https://colinhacks.com/essays/building-a-spa-with-nextjs) showing how to integrate `react-router` with Next.js. If that still doesn't meet your needs (_main drawback: lack of initial SSR_) than it's sadly time to say goodbye to this framework.

But what do I mean by the router not supporting SPA navigation? Next.js either creates the pre-built HTML for a page (SSG) or uses server-side rendering to deliver it. The second option can use dynamic runtime path parameters, while the first works only with known paths even if those are fetched dynamically during the build step. This way you can't deliver "truly" SPA style, dynamic, client-side transitions, only if you use query parameters instead of the URL path, like this:

❌ `/product-category/shoes/1` => ONLY SSG OR SSR
✅ `/product-category?category=shoes&page=1` => YOU IMPLEMENT CSR

If you are interested in a real life example where this can really make a difference, check out [this article](https://www.singlestore.com/blog/moving-from-next-js/) from _**SingleStore**_ about why they moved away from Next.js, specifically the _Dynamic Routing_ part.

## Vercel Telemetry 
> Thanks [onems](https://www.reddit.com/user/onems/)!

Some developers and companies might be unaware of the fact that every Next.js project sends live statistics to Vercel, a private company unless you **opt-out** of the collection. This is questionable, especially in case of an open source project. If you are interested in what exactly is collected and how to disable it Florian wrote and [insightful post](https://florian-martens.medium.com/how-to-stop-your-next-js-app-from-sending-telemetry-data-to-vercel-cf499c773f94) about it on Medium.

## Do you want to learn more _less-known facts_ and _behind-the-scenes_ about Next.js?
Then check out my complete [Full Context Next.js Review](https://www.fullcontextdevelopment.com/blog/next-full-context-review) which goes into waaay more detail. It's long (_the table of contents help_), by now is a bit outdated as it's about v.12 but I'm sure there's a lot you would find interesting, relevant and useful.

---

I also post similarly insightful articles on my [Substack](https://fullctxdev.substack.com/) page and newsletter.

![Substack newsletter cover image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyrkoofy5ny8868d6jz7.jpg)