Next.js 9.1

Today, we are excited to announce Next.js 9.1 with src and public directory support.

New in This Release

  • src Directory Support: The pages directory can now be nested under the src folder, supporting a wider variety of application setups.
  • public Directory Support: Define files to be mounted at the root of your application's URL (e.g. favicon.ico).

Previewing in This Release

  • Built-in CSS Support: Applications can soon import Global CSS and leverage development hot reloading and advanced production optimizations, compilation, and polyfilling.
  • Static Error Pages: Statically export expected error pages (like 404) for better availability (CDN).
  • Module / Nomodule: Ship smaller JavaScript bundles to end-users running on evergreen browsers.
  • Improved Bundle Splitting: Ship fewer bytes to your end-user, improving TTI and page transition speed. Large library chunks are also long-term cached across deploys.

As always, we strive to ensure all of these benefits are backward compatible. All you need to do to update is run:

npm i next@latest react@latest react-dom@latest

If your application is on a version of Next.js before 9 you can reference the upgrade guide for changes you may need to upgrade.

Since our last major release, we are happy to have seen companies like TikTok, Hilton, Elastic, Realtor, and JW Player launch with Next.js. Check out the showcase for more!

Next.js has a special pages directory where every file becomes a separate route, following the convention over configuration approach this directory is required to be in the root of a Next.js application.

In talking with companies using Next.js and inspecting some closed source codebases we established a common pattern developers wanted is to have a src directory for their code and have the pages directory within it also.

Starting from Next.js 9.1, it's now possible to create a src/pages directory instead of creating a pages directory in the root of the application.

Using the src directory is optional and covers the case where your company has this standard in place already.

The pages folder in the src directory directory

Besides the pages directory Next.js has one other special directory called static, files of which were mapped to the /static route.

For example, static/my-image.png would be mapped to /static/my-image.png.

This convention has worked out well ever since the first release of Next.js and doesn't have problems in particular.

However, over time we have established that /static doesn't cover everything needed in a web application. For example, robots.txt has to be served from the root of the domain.

Starting from Next.js 9.1 we are introducing a new directory called public.

Any files in the public directory will be mapped to the root of the domain.

For example, public/robots.txt will be mapped to /robots.txt.

Because public also covers the static directory use case we have decided to deprecate the static directory in favor of creating a public/static folder with the same functionality.

As always we strive for backward compatibility so the static directory will continue to work with a deprecation message.

The following features are currently under an experimental flag and will be released once the final implementation is ready.

Currently, Next.js has a CSS-in-JS solution built-in called styled-jsx. This solution works well for styling individual React components.

However, when talking to companies we found there is a common need to reuse some existing styling or design system based around CSS.

In general, this means adding in the next-css plugin to add CSS imports support.

We found that around 50% of all Next.js users add this plugin to their application.

Because of this extensive usage, we are adding built-in support for CSS imports with automatic reloading of styles in development and production optimizations that were previously not possible in the next-css plugin.

The initial implementation is currently being tested on production Next.js applications.

Global CSS imports will be introduced first:

// pages/_app.js

// Global styles can be imported from _app.js
import '../styles/global.css'
import App from 'next/app'

export default App

After global CSS imports we'll introduce support for CSS modules through the .module.css extension:

// pages/index.js

// Scoped styles are imported through .module.css
import styles from '../styles/index.module.css'

export default function HomePage() {
  return <h1 className={styles.heading}>Hello World</h1>
}

This will allow us to provide a significantly better developer experience when using CSS imports.

You can read more about the ongoing work on the RFC on GitHub.

Next.js has a special page rendered when an error happens, this page is internally called /_error. This page can be customized by users by creating a pages/_error.js file that exports a React Component.

The rendered errors are generally divided into 2 cases: expected errors and unexpected errors.

Expected errors are, for example, the 404 page.

An unexpected error would be, for example, when an error is thrown in getInitialProps or while rendering the React tree, which renders a 500.

We are planning to add the automatic static optimization for expected errors as in general, they do not have to be dynamically rendered.

There will be an opt-out if a user wants to dynamically render these pages but the default will be that 404 will be a static page. This reduces the load on the server when using the server target and reduces cost when using the serverless target.

Another benefit of making the page static is that it can be cached automatically when using a CDN.

As shared in the Next.js 9 announcement the Google Chrome team is investing resources into improving Next.js and has been working on multiple efforts to make large-scale performance improvements for all Next.js applications.

To learn more about this collaboration you can read the Next.js 9 announcement and watch this talk by Nicole Sullivan at React Rally.

When writing code in Next.js you generally write “modern” JavaScript. You can use all the latest stable features and Next.js will automatically ensure that these features are transformed or polyfilled through compiling the code using Babel.

At this point, many of these JavaScript features are supported in the majority of browsers. However, in general, (including in Next.js) code is shipped as a single JavaScript bundle that runs on all browsers that your application supports.

In the case of Next.js, this means compiling the modern JavaScript down to a format that is compatible with Internet Explorer 11.

For example, currently, Next.js has to provide polyfills for async/await syntax as code might be executed in browsers that do not support async/await which would break.

To avoid breaking older browsers while sending modern JavaScript to browsers that support newer syntax Next.js will utilize the module/nomodule pattern. The module/nomodule pattern provides a reliable mechanism for serving modern JavaScript to modern browsers while allowing older browsers to fall back to polyfilled ES5 code.

This new feature is currently being tested in production by multiple large-scale Next.js applications to collect real-world data. The results of these tests are looking promising and more details on the performance improvements of this change will be shared soon.

One example of initial results we have seen is this comparison by Natalie Marleny about the bundle sizes before and after enabling built-in module/nomodule support in Next.js:

Next.js currently has multiple JavaScript bundles it loads to make a page interactive. Most notably:

  • The page JavaScript bundle.
  • A file with common JavaScript.
  • Next.js client-side runtime bundle.
  • Dynamic imports (generally added through next/dynamic).

To ensure the page gets interactive, all these bundles have to load as they all depend on each other to be able to boot up React in the browser.

Because these bundles are required to be loaded for React to boot up, it’s important that they are as optimized as possible and don’t over-download too much code from the rest of the application.

For this reason, there is a commons bundle that holds common JavaScript between pages. The calculation of the current bundle-splitting strategy to generate commons is based around a ratio-based heuristic, if a module is used in 50% of all pages it’ll be marked as a common module.

However, applications could exist out of many different pieces. For example, marketing pages, a blog, and a dashboard. If there was a large number of marketing pages compared to the dashboard the commons calculation would cause a more optimized result for the marketing pages.

Our goal is to optimize both of these at the same time in one application.

Alex Castle defined a new layer of chunking (creation of separate JavaScript files) that allows more optimized commons chunking with multiple files and especially when many pages are involved.

Similar to the module/nomodule support the improved bundle splitting is being tested in production by multiple large-scale Next.js applications to collect real-world data. The results of these tests and more details on the performance improvements of this change will be shared soon in a separate blog post.

We are excited about the upcoming changes that will improve performance across all Next.js applications.

Furthermore, the Next.js community is still expanding:

  • We have had over 800 contributors landing at least 1 commit.
  • On GitHub, the project has been starred over 41,350 times.
  • The examples directory has over 210 examples.

The Next.js community now has over 11,250 members. Join us!

We are thankful to our community and all the external feedback and contributions that helped shape this release.