What is Next.js App Router & Is it ready for the main stage?

Kolby Sisk
Udacity Eng & Data
Published in
7 min readMay 9, 2023

--

Next.js recently released version 13.4, which officially marks App Router stable. App Router is a code name for the new paradigm in which Next.js applications can be built. Previously called App Dir when it was released as a beta, with Next.js 13, this new paradigm introduces an array of enhancements that have enthralled the community, almost to the point of exhaustion.

I’ve been an early adopter of Next.js App Router and have been delighted with the improved developer experience, rendering performance, and mental model that comes with it.

I’ll absolutely use Next.js App Router for every side project I start, but now that it is officially stable — is it ready for a large-scale production project?

Features of App Router

Before we answer that question, let’s look into the features that make it compelling versus the classic Next.js Pages Router.

✨ React Server Components

I won’t bury the lede here, the main selling point for App Router is its use of React’s new Server Component architecture.

Similar to how React changed the way we think about building UIs, React Server Components introduce a new mental model for building hybrid applications that leverage the server and the client.

Server Components allow you to define components that execute on the server during rendering, and then stream updates to the client. This process provides benefits such as improved initial load times, reduced JavaScript bundle sizes, and improved SEO.

✨ Streaming and Suspense

As mentioned, Server Components will stream updates to the client, which means the initial page load will be much quicker, and users can start interacting with the page without waiting for the entire page to load.

The code example above shows an example of using a Server Component that makes use of React Suspense. The Posts Server Component will initially render PostListSkeleton as the loading state. Next, notice how the PostList component is asynchronous. Once the promise resolves, the rendered contents of the component will be streamed to the client, replacing the loading state.

The best part? No more fetching data on the client. No more fetching data in a useEffect. No more unintuitive fetching hook libraries. All data is fetched on the server now. Next.js even monkey patched Fetch to add caching functionality, which they use to deduplicate requests. This allows us to fetch data directly in the component that needs it, using the native Fetch API.

✨ Server Actions (Alpha)

Next.js 13.4 introduced Server Actions — a solution for handling mutations on the server that can be called directly from the client. Server Actions integrate with the rest of the data lifecycle and can mutate data, revalidate any existing cache of that data, and then re-render the page in a single network roundtrip.

The above code shows just how simple using a Server Action is. There is a Server Action named saveContact. It is notated as a Server Action by the use server directive. Then we set the action of the form to the name of our Server Action and when the form is submitted it will invoke the Server Action. Since our Server Action runs on the server we can make use of secrets or make calls directly to a database.

✨ Nested Routes & Layouts

Next.js has always had a unique way of handling routing by leveraging the power of the file system.

App Router improves on the previous Page Router implementation by including new features like:

  • Layouts that can be shared between multiple pages.
  • Route Groups that allow you to organize routes without affecting the URL, and opting-in specific routes into a layout.
  • Loading UI that helps you create meaningful loading states.
  • Intercepting Routes that allow you to load a route in different ways depending on the context.

That’s only scratching the surface, there are so many more features included. I recommend checking out the official docs to learn more.

✨ Built-in SEO

The final feature we’ll look at is the new Metadata API which is used to improve SEO. It includes all the SEO configuration you might need including the Open Graph protocol. There are two ways to use it, either statically, or dynamically.

Static SEO example
Dynamic SEO example

Notice how in the example above getProduct is called multiple times. As we mentioned previously, Next.js is able to deduplicate requests, so the API is called only once.

Ready for production?

Obviously I’m sold on Next.js App Router, but is it ready for large-scale applications in production? I recently conducted an experiment to find out by attempting to refactor a production app from Next.js Pages Router to App Router. Here are some problems I faced.

🚩 Mocking Problem

The first problem I faced was getting mocking to work. I subscribe to Kent C. Dodds testing philosophy of:

Write tests. Not too many. Mostly integration. (Quote actually comes from Vercel’s CEO Guillermo Rauch)

When writing integration tests, you need a solution for mocking. Recently, the frontend testing world has fallen in love with the mocking solution MSW. It works by intercepting requests using service works and returning a mock payload that you define. It does this without the need to spin up a node server, which simplifies much of the testing setup, and comes with a pleasant developer experience.

Sadly, when I tried using MSW with Next.js App Router — it didn’t work. I turned to Twitter expecting this to be a common problem that would have a solution, but sadly that was not the case:

💡 Mocking Solution

Bummer! Unfortunately, this is a show stopper for me, but if you want to use Next.js App Router today there are other options. Instead of MSW, you could use one of the mocking solutions that utilizes a Node server rather than intercepting. Another option is to utilize E2E testing rather than integration testing. I wouldn’t let a shiny new toy determine the testing strategy you use for your app though.

🚩 Emotion Problem

For any large-scale app that I work on, I make sure to use a component library like Chakra, Mantine, or MUI. They provide the necessary accessibility features that are required for large-scale work. These — and any other — Emotion based component libraries do not currently support Server Components. When trying to use a component from one of these libraries in a Server Component you’ll be faced with an error like:

You’re importing a component that needs useRef. It only works in a Client Component but none of its parents are marked with “use client”, so they’re Server Components by default.

The root of this problem is the runtime CSS-in-JS that comes with @emotion/styled. CSS-in-JS is what enables the Style Props feature that greatly contributes to making these component libraries the powerful tools they are. The creator of Chakra recently wrote a blog post about the future of Chakra where he describes this problem and the work that the Chakra team is doing to fix it. TL;DR they are working on a new framework-agnostic styling solution named Panda. This new solution will enable zero runtime CSS-in-JS by extracting styles at build time. It’s still in an early experimental phase though, so if you’re a Chakra fan like me you’ll need to wait a bit longer before it’s ready.

💡 Emotion Solution

There are workarounds for using these libraries in your project, but you’re limited to using them only in Client Components. This isn’t ideal, but it works with minimal consequences. One solution that I’ve been using, that works seamlessly is Tailwind CSS. Because Tailwind uses a zero-runtime solution, similar to what the Chakra team is building, it works just fine. Tailwind doesn’t come with all the benefits of a component library though. It does not include accessibility features, it is just a styling tool, so along with Tailwind you’ll need to include a Tailwind based component library like Headless UI, Daisy UI, or Radix UI.

Conclusion

So is Next.js App Router ready for large-scale production applications? In the case of Udacity, the answer is no. At Udacity we make use of MSW and Chakra for our Next.js applications, and due to the lack of comprehensive support for both it doesn’t make sense for us to start using App Router, yet. There is also an inherit risk in being an early adopter that is more suitable for smaller-scale applications. If you’re comfortable taking on the risk, and the problems mentioned do not pose significant obstacles for your application, then App Router is definitely worth using.

If you want to keep up with the latest in React & Next.js make sure to follow me here and on Twitter @Kolbysisk. 🙏

--

--

Builder of software with a passion for learning. I specialize in web development and user experience design. www.kolbysisk.com