Efficiency and productivity are critical in modern web development. That’s why Next.js introduced the Hot Module Replacement (HMR) mechanism, more commonly known as Fast Refresh.
The HMR feature ensures that any edits you make in your code are quickly reflected in the browser without requiring a full page reload, enhancing your development experience as a result.
While Next.js HMR is powerful, also comes with some challenges. In this article, we’ll dive into the complex world of Fast Refresh to understand how it works. Then, we’ll explore how to address common HMR issues where the Next.js hot reload feature does not work as expected.
Jump ahead:
HMR, or Fast Refresh, is a Next.js feature for local development servers. It provides instantaneous feedback every time you edit a React component on the page, re-rendering only the components that have been updated. Here’s an example of Next.js HMR in action:
With HMR, most changes should be visible within a second and without affecting the current component state. This is because the feature is smart enough to preserve the local state of the components to re-render.
Next.js dev
servers have supported HMR since version 9.4, which was released in 2020.
Fast Refresh attempts to hot reload the updated components. If this isn’t possible, it performs a full reload of the entire page as a fallback plan.
There are two actions that trigger Next.js hot reload:
Changes to other files are either ignored or lead to full page reloads.
Keep in mind that HMR is resilient to runtime errors. If one of your components causes such an error, Next.js will show an overlay like so:
Fixing the error will automatically dismiss the overlay without resulting in a page reload. If the error did not occur during rendering, Next.js will retain the component state. Otherwise, the page will be remounted using the updated code and the state will be lost.
Before worrying about Next.js HMR, make sure you are not experiencing a caching issue. Next.js builds the local server in the .next
folder and sometimes fails to update it properly. As a general solution, follow these steps:
dev
server.next
folderdev
server again with next dev
If this is not enough to solve the problem or you need to repeat the above procedure, then chances are that you have a real HMR issue.
Let’s now look at some of the most common scenarios where Next.js hot reload does not work as expected.
In most cases, Next.js HMR manages to preserve the local state of React components between edits. In particular, values in useState
and useRef
variables are retained as long as you do not change their arguments or the order of the Hook calls.
However, that is not always true. In some cases, Next.js HMR might fail to preserve the component state. Let’s go through a few common scenarios where Next.js will end up resetting the local state or even doing a full reload.
One issue could occur when you edit a class component or a functional component whose export is wrapped by a higher-order function that returns a class. The solution in this case is to use only function components with Hooks.
Another issue may happen when you edit a component file that exports some values imported by files outside of the React tree. For example, a React component that exports a constant used by a non-React utility function.
As a solution, you should restructure your exports to avoid this scenario by migrating the extra exports in the component file to a separate file. Then, fix the imports accordingly.
Next, imagine that you edit an anonymous arrow function component as below:
export default () => { return <div>{/*your content...*/}</div> }
Anonymous functions can cause issues with HMR if you have multiple components defined this way. Instead, replace anonymous arrow function components with named components so Next.js HMR can use the display name to determine which module to replace:
export default function MyComponent() { return <div>{/*your content...*/}</div> }
You can use the Next.js name-default-component
tool to automatically transform anonymous components into named components.
The goal of Fast Refresh is to improve developer productivity when they work on a local server. Enabling it in production applications would only waste resources without providing any real benefit. This is why it is disabled by default when not running Next.js on a local server.
If there is something wrong with your app, check if Next.js HMR is enabled in production by taking a look at the memory usage graph from your hosting provider. If you notice some regular spikes associated with calls to _next/webpack-hmr
, then Fast Refresh is getting called in production.
The most common reasons behind the problem are:
next
command instead of next start
Thus, make sure to deploy a production build with next build
and launch your application with next start
.
next-i18next
is the most popular i18n library for building internationalized Next.js apps. By default, it stores translations in a .json
file under the /public
folder as follows:
If you add a new translation or update an existing one when developing locally, nothing will happen. Fast Refresh will not trigger and the page will not reflect the update. The same happens even if you manually perform a full reload on the page. To see the updates, you must relaunch the local server.
This is because Next.js HMR only works for updates to React components in files imported by React components. The i18n translation files do not fall into this category.
To address the issue, add the following lines to your pages/_app.jsx
or app/layout.jsx
component:
const { i18n } = useTranslation() // reloading the i18n resources on page reload // if we are developing locally if (process.env.NODE_ENV === "development") { i18n .reloadResources() .then(() => () => { console.log("i18n translations reloaded") }) .catch((e) => { console.error(e) }) }
The above snippet gets the i18n
variable from the useTranslation()
Hook. Then, it calls the reloadResources()
method on it to reload the translation resources. Since this operation involves some overhead, it should only be performed on a local server.
Now, every time you manually perform a full reload with the refresh button of the browser or the Cmd + R
shortcut, the i18n translation files inside /public
will be loaded. This way, you can see the updated translations without having to kill the local server and execute the next dev
command again.
next.config.js
do not propagatenext.config.js
is the default Next.js configuration file. It accepts several settings that allow you to deal with redirects, rewrites, custom HTTP headers, and more.
If you edit this file when running your application locally, Fast Refresh will simply ignore the updates. As before, this is because next.config.js
is not a React component or a file imported by a React component, so it does not meet the requirements to trigger a hot reload.
Not all developers are aware of this and may think they are encountering a bug. However, you can determine if you’re experiencing this problem easily because every time you change something in next.config.js
, Next.js will warn you with the following message:
Found a change in next.config.js. Restarting the server to apply the changes...
This invites you to stop the local server and launch it again to see the effects of your updates.
Similarly, Fast Refresh will ignore edits to other configuration files, like postcss.config.js
. If you change these files, the site will not update accordingly and Next.js will not even trigger a warning message like the above. You will have to manually relaunch the development server to see the changes.
Your Next.js application is likely to read configurations and constants from the project’s .env
files. An issue with older versions of Next.js is that a change in those files doesn’t trigger a hot reload. When you edit an environment variable or add a new one, nothing will happen.
However, as of Next.js v12.3, the following files have been added to the Fast Refresh feature:
.env
and env.*
variantsjsconfig.json
tsconfig.json
This means that you are no longer required to restart the local development server when making changes to the above configuration files. Follow the official migration guide to upgrade Next.js to the latest version and benefit from this new feature.
middleware.js
with the App RouterA flash of unstyled content (FOUC) happens when a web page is displayed with the browser’s default styles for an instant before loading its CSS styles. This is usually a bug and should never happen in a Next.js application.
As GitHub user timfee
discovered, you can reproduce this FOUC issue in Next.js if you do the below:
layout.jsx
and page.jsx
component in a project using the App Router filesystemnext dev
middleware.js
filelayout.jsx
You can see from the GIF below — which was provided by timfee
in the issue he opened on GitHub — that every edit causes an FOUC:
When middleware.ts
is present, Fast Refresh seems to remove and re-add styles to the page every time you change a React component. This is a bug in the Next.js hot reload logic.
To fix the problem, update Next.js to version 13.3 or higher with the command below:
npm i next@latest react@latest react-dom@latest eslint-config-next@latest
The Next.js HMR feature is great, but what if you want to temporarily disable it while developing locally? At the end of the day, the refreshes may be annoying when working on the layout or styling components.
Unfortunately, there is no official way to disable Fast Refresh in Next.js. The VP of Developer Experience at Vercel, Lee Robinson, stated on GitHub that “We are not planning to allow turning off Fast Refresh as it’s a critical part of Next.js that we strongly encourage using.”
Thus, there is no setting to turn HMR off when running a Next.js application with next dev
. The community has found some workarounds, but most of them no longer work. Since disabling Fast Refresh is discouraged by the official team, you should not do it.
In this article, you learned what Fast Refresh is in Next.js, how it works, and how to tackle the most common issues related to it. You now know that Fast Refresh provides hot module replacement to Next.js projects and when it is triggered in development.
The feature is powerful and stable, but its complex nature makes it prone to minor errors that might hinder the development experience. As seen here, most of them can be easily addressed with simple configurations or workarounds.
Debugging Next applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your Next.js app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your Next.js apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowIn web development projects, developers typically create user interface elements with standard DOM elements. Sometimes, web developers need to create […]
Toast notifications are messages that appear on the screen to provide feedback to users. When users interact with the user […]
Deno’s features and built-in TypeScript support make it appealing for developers seeking a secure and streamlined development experience.
It can be difficult to choose between types and interfaces in TypeScript, but in this post, you’ll learn which to use in specific use cases.