October has come and gone, and with it, Next.js has released a new major version packed (pun intended) with tons of new features — some of which can be seamlessly adopted from your Next.js 12 app, while others not so much.
If you’re just jumping on the bandwagon, it may be confusing to distinguish the hype, the misinformation, and what’s stable for your production apps, but fear not! I’m here to give you a nice overview and get you up to speed.
What Misinformation?
As with all Next.js releases, there are a few APIs that are moved into the stable core and for recommended use, and there are others still in the experimental channel. “Experimental” APIs are still up for debate. The main functionality is there, but the question of how these APIs behave and how they can be used is still susceptible to change as there may be bugs or unexpected side effects.
In version 13, the experimental releases were big and took over the spotlight. This caused many people to consider the whole release unstable and experimental — but it’s not. Next.js 13 is actually quite stable and allows for a smooth upgrade from version 12 if you don’t intend to adopt any experimental API. Most changes can be incrementally adopted, which we’ll get into detail later in this post.
Releases Summary
Before we dig deeper into what each announcement entails, let’s check on a quick list and balance experiments and stable features.
Experimental
- App Directory;
- New Bundler (Turbopack);
- Font Optimization.
Stable
- “New” Image Component to replace legacy
Image
component as default; - ES Module Support for
next.config.mjs
; - “New”
Link
component.
The App Directory
This feature is actually a big architectural rewrite. It puts React Server Components front and center, leverages a whole new routing system and router hooks (under next/navigation
instead of next/router
), and flips the entire data-fetching story.
This is all meant to enable big performance improvements, like eagerly rendering each part of your view which doesn’t depend on data while suspending (you read that right!) the pieces which are fetching data and getting rendered on the server.
As a consequence, this also brings a huge mental model change to how you architect your Next.js app.
Let’s compare how things were versus how they will work in the App directory. When using the /pages
directory (the architecture we have been using up to now), data is fetched from the page level and is cascaded down toward the leaf components.
In contrast, given that the app directory is powered by Server Components, each component is in charge of its own data, meaning you can now fetch-then-render every component you need and cache them individually, performing Incremental Static Regeneration (ISR) at a much more granular level.
Additionally, Next.js will carry on optimizations: Requests will be deduped (not allowing different components to fire the same request in parallel), thanks to a change in how the fetch
runtime method works with the cache. By default, all requests will use strong cache heuristics (“force-cache”), which can be opted out via configuration.
You read it right. Next.js and React Server Components both interfere with the
fetch
standard in order to provide resource-fetching optimisations.
You Don’t Need To Go “All-In”
It is important to point out that the transition from the /pages
architecture to /app
can be done incrementally, and both solutions can coexist as long as routes don’t overlap. There’s currently no mention in Next.js’ roadmap about deprecating support for /pages
.
Recommended Reading: ISR vs DPR: Big Words, Quick Explanation by Cassidy Williams
New Bundler And Benchmarks
Since its first release, Next.js has used webpack under the hood. This year, we have watched a new generation of bundlers, written in low-level languages, popping up, such as ESBuild (which powers Vite), Parcel 2 (Rust), and others. We have also watched Vercel setting the stage for a big change in Next.js. In version 12, they added SWC to their build and transpilation process as a step to replacing both Babel and Terser.
In version 13, they announced Turbopack, a new bundler written in Rust with very bold performance claims. Yes, there has been controversy on Twitter about which bundler is the fastest overall and how those benchmarks were measured. Still, it’s beyond debate how much Turbopack can actually help large projects written in Next.js with way better ergonomics than any other tool (for starters, with built-in configuration).
This feature is not only experimental but actually only works with
next dev
. You should not (and as of now can’t ) use it for a production build.
Font Optimization
The new @next/font
module allows making performance optimization to your Web Fonts during build time. It will download the font assets during build-time and host them in your very own /public
folder. This will save a round-trip to a further server, avoid an additional handshake, and ultimately deliver your font in the fastest way possible and cache it properly with the rest of your resources.
Remember that when using this package, the it’s important to have a working internet connection when you run your development build the first time so it can cache it properly, otherwise it will fallback to system fonts if adjustFontFallback
is not set.
Additionally, @next/font
has a special module for Google Web Fonts, conveniently available as they are widely used:
import { Jost } from '@next/font/google';
// get an object with font styles:
const jost = Jost();
// define them in your component:
<html className={jost.className}>
The module will also work in case you use custom fonts:
import localFont from '@next/font/local';
const myFont = localFont({ src: './my-font.woff2' });
<html className={myFont.className}>
Even though this feature is still in Beta, it is considered stable enough for you to use in production.
New Image And Link Components
Arguably the most important components within the Next.js package have received a slight overhaul. Next Image
has been living a double life since Next.js 12 in @next/image
and @next/future/image
. In Next.js 13, the default component is switched:
next/image
moves tonext/legacy/image
;next/future/image
moves tonext/image
.
This change comes with a codemod, a command that attempts to automigrate the code in your app. This allows for a smooth migration when upgrading Next.js:
npx @next/codemod next-image-to-legacy-image ./pages
If you make this change and do not have visual regression tests set up, I’d recommend taking a good look at your pages in every major browser to see if everything looks correct.
For the new Link component, the change should also be smooth. The <a>
element within <Link>
is not necessary nor recommended anymore. The codemod will either remove it or add a legacyBehavior
prop to your component.
npx @next/codemod new-link ./pages
In case the codemod fails, you will receive a linting warning on dev, so keep an eye on your terminal!
ES Modules and Automatic Module Transpilation
There two upgrades have passed under the radar for most, but I consider them especially useful for people working with Monorepos. Up until now, it was not very ergonomic to share configuration between configuration files and other files that may be used in runtime. That’s because next.config.js
is written with CommonJS as the module system, which can’t import from ESM files. Now, Next.js supports ESM simply by adding type: "module"
to your package.json
and renaming next.config.js
→ next.config.mjs
.
Note: The “m” stands for “module” and is part of the Node.js spec for ESM support.
For Monorepos using internal packages (JavaScript packages that are not published to NPM but instead are consumed from source by sibling apps within the monorepo), a special plugin was necessary to transpile those modules on build-time when consuming them. From Next.js 13 onwards, this can be arranged without a plugin by simply passing an (experimental) property to your next.config.mjs
:
const nextConfig = { experimental: { transpilePackages: ['@my-org/internal-package'], },
};
You can see an example in the Apex-Monorepo template. With these settings, it is possible to develop both the dependency component and your app simultaneously without any publishing or workaround.
What’s Next?
If you’re still interested in playing around and talking more about these features, I’ll be running a Advanced Next.js Masterclass from Nov 30 – Dec 15, 2022 — I’d be super happy to welcome you there and answer all of your questions!
Until then, let me know in the comments below or tweet over to me at @AtilaFassina on how your migration has been and your thoughts on the experimental features.
(vf, yk, il)