Matt Angelosanto
Posted on August 23, 2023
Written by Kingsley Ubah✏️
Next.js is presently one of the fastest-growing web frameworks in terms of adoption. In addition to features like static site generation, server-side rendering, and file routing, the Next.js team continually adds new features to make building highly optimized and performant web applications super easy.
Despite the many benefits and features Next.js offers to developers, as with any framework, you might run into errors in your code during development. Next does a good job of logging helpful messages, but you might still find some errors hard to debug.
This article explores the causes and solutions of some common errors in Next.js, including:
- Next.js hydration errors
- Document or window object error
- Build failure due to webpack errors
- API and slug-related errors
- Error when importing modules
- CORS error — Debugging Next.js API routes
- Catching all errors in your Next.js app
Let’s jump right in.
1. Next.js hydration errors
One common error you’re likely to encounter in both Next and React apps is the hydration error. Hydration errors result from a mismatch between server- and client-rendered markup and differences in component states.
Specifically, Next.js hydration errors arise when you wrap your components or HTML elements with an improper tag. A common example is when you have a p
tag wrapping your divs, sections, or other elements.
The exact error message you’ll get is:
Hydration failed because the initial UI does not match what was rendered on the server
To fix the error, you need to check the markup throughout your application and ensure that you’re not wrapping elements or custom components with improper tags.
For example, the following component will result in a Next.js hydration error because we wrapped the div
element inside the paragraph:
import Image from 'next/image'
export const ErrorComponent = ()=>{
return(
<p>
<div>Don't do this!</div>
<Image src='/logo.jpg' alt='' width='40' height='40'/>
</p>
)
}
Rather, use the right “wrapper” elements — e.g. div
, section
, main
, etc. — to wrap your content:
export const CorrectComponent = ()=>{
return(
<div>
<div>Do this instead</div>
<Image src='/logo.jpg' alt='' width='40' height='40'/>
</div>
)
}
Keep in mind that some third-party components, such as those from MUI, may use the <p>
tag as a top-level element. In such cases, you’ll need to wrap the component inside something semantic to avoid the error, like a <div>
or <section>
.
Properly arranging your HTML will greatly reduce the likelihood of encountering the hydration error in your Next app, but it’s not the only cause. You can also encounter the error after importing and running certain packages, as we will explore in the next section.
2. Document or window object error
When you try to access the window object while the component is still mounting, Next.js will throw an error that the document or window is not defined. This error also occurs when you install and use a library that tries to access the window object before the component mounts.
Suppose that you have a home.js
file in your Next.js application. Attempting to access the local storage will result in the window is not defined
error:
const Home = () => {
const saveSession = (event) => {
window.sessionStorage.setItem("key", event.target.value)
}
return (
<div>
<input
type="text"
id="message"
name="message"
onChange={saveSession}
value={message}
/>
</div>
);
};
export default Home;
This is the same with third-party libraries. For example, let’s say you installed the swiperjs
package and the library then tries to access the window object internally. Using swiperjs
as demonstrated in the code below will also result in the same error:
import { Swiper } from 'swiperjs'
const Home = () => {
return (
<>
<Swiper
spaceBetween={50}
slidesPerView={3}
navigation
pagination={{ clickable: true }}
>
// Slides go here
</Swiper>
</>
);
};
export default Home;
This error occurs because Swiper
is rendered before it can access the window object. To resolve this error, you need to execute the code that accesses the browser’s window object inside the useEffect()
Hook. That way, the code only executes after the component has been mounted.
Here’s how you’d rewrite the first example for it to work:
const Home = () => {
const saveSession = (event) => {
useEffect(() => {
window.sessionStorage.setItem("key", event.target.value)
})
}
return (
<div>
<input
type="text"
id="message"
name="message"
onChange={saveSession}
value={message}
/>
</div>
);
};
export default Home;
If you’re using a library that accesses the window object, you need to load the library only after the component has been mounted. For example, this is how you’d load Swiper
to avoid the error:
const Home = () => {
const [domLoaded, setDomLoaded] = useState(false);
useEffect(() => {
setDomLoaded(true);
}, []);
return (
<>
{domLoaded && (
<Swiper
spaceBetween={50}
// ...
>
<div>Test</div>
</Swiper>
)}
</>
);
};
export default Home;
3. Build failure due to webpack errors
After running the next build
command to build your Next.js application, you might get an error message stating that the build failed because of webpack errors:
Cannot read property 'asString' of undefined
> Build error occurred
Error: > Build failed because of webpack errors
Next.js v11 and newer use webpack 5 by default. However, this error only occurs with webpack 4.
To resolve the issue, you need to enable webpack 5 in your Next.js application. You can do this by adding the following setting in next.config.js
:
module.exports = {
// Add this:
future: {
webpack5: true,
},
};
4. API and slug-related errors
A large number of errors in Next.js are related to APIs and slugs. Next.js provides two APIs for data fetching — getStaticPaths
and getServerSideProps
. However, these functions can throw an error if not properly used.
Here's an error you might often get with the getStaticProps
API:
Error: getStaticPaths is required for dynamic SSG pages and is missing for '/productsPage/[slug]'
You get this error when you render a page on the server side with the SSG feature, but fail to define the getStaticProps
function in the page’s component.
This is because Next.js requires the getStaticPaths
function to build server-side rendered or statically generated pages that use dynamic routes. So, the solution is simply to add a getStaticPaths()
function to the page component for /pageName/[slug]
.
Let’s see an example. In a hypothetical ecommerce store, the /pageName/[slug]
page could show the details of a particular product.
In that case, you can use the getStaticPaths
function to retrieve a list of all the available product slugs from a database, then return the list to be used for the [slug]
parameter:
export async function getStaticPaths() {
const res = await fetch('http://localhost/path/to/products')
const products = await res.json()
const slugs = products.map(post => product.slug)
return {
paths: slugs.map((slug) => ({ params: { slug } })),
fallback: false
}
}
5. Error when importing modules
When importing modules into your Next.js application, you might run into the Module not found
error:
Module not found: Can't resolve 'fs'
This means that Next.js is unable to locate the module specified in the error message. If you’re using a local module, then you need to specify its correct path. But if it's an npm or Yarn module, the error usually occurs because the module isn’t available on the client side.
One way to fix this issue is to ensure that all Node.js and server-related code is placed inside of the Next.js data-fetching APIs — getServerSideProps
, getStaticPaths
, or getStaticProps
:
export function Home({ fileInfo }) {
return (
<div>
{/* page content */}
</div>
);
}
export default Home;
export async function getServerSideProps(context) {
const util = require('util');
const tring = util.format(1, 2, 3);
return {
props: {
fileInfo,
},
};
}
If that fails to resolve the issue, then you might want to cross-check that the letter casing of the file you’re attempting to import is correct. Here’s an example:
// components/MyComponent.js
export default function MyComponent() {
return <h1>Hello</h1>
}
When importing the above component, you need to use the exact letter case of the filename:
// pages/index.js
// This will not work
import MyComponent from '../components/Mycomponent'
// This will work
import MyComponent from '../components/MyComponent'
Note that incorrect letter casing will only lead to Module not found
errors in case-sensitive environments.
6. CORS error — Debugging Next.js API routes
The /pages/api
directory is where you put all the files responsible for handling API requests, with the file name corresponding to the endpoint. Next.js then automatically maps these files to the /api/*
URL, which you can access within the application via asynchronous API requests.
Once you deploy your app to the internet, the app will exist on a different origin. As a result, attempting to access the API endpoint will result in a CORS error as shown below:
Access to fetch at 'http://example.com/api/test' from origin 'http://localhost:3000' has been blocked by CORS policy
To fix the CORS issue, you need to use CORS to handle cross-origin requests. Start by installing cors
:
npm install cors
# OR
yarn add cors
Once installation completes, you can import the library and use it to execute custom middleware just before the API’s response is received. Here’s an example where we set up the POST, GET, and HEAD methods for this endpoint:
// pages/api/test.js
import Cors from "cors";
const cors = Cors({
methods: ["POST", "GET", "HEAD"],
});
function middleware(req, res, fn) {
return new Promise((resolve, reject) => {
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export default async function runMiddleware(req, res) {
await middleware(req, res, cors);
// Other API logic
res.json({ result: result });
}
With this code, we can make all four types of API requests to a different origin successfully without encountering the CORS error.
7. Catching all errors in your Next.js app
When creating your Next.js application, it’s important to catch all errors and make sure each of them is handled. The best way to do this is at the component level using the try...catch
syntax.
In the following example, if any error is encountered while fetching data from the API, we log the error, render the built-in error page, and pass in a 503 status code:
import fetch from "isomorphic-fetch"
import Error from "next-error"
const Home extends React.Component {
static async getInitialProps() {
let products = [];
try {
const resp = fetch("https://my-store.com")
products = await resp.json()
} catch(err) {
console.log(err)
}
return { products }
}
render () {
const { products } = this.props
if(products.length === 0) {
return <Error statusCode={503} />
}
return (
<div>
{products.map((product) => {
<h3 key={product.id}>{product.title}</h3>
})}
</div>
)
}
}
To learn more about catching and resolving errors in Next.js, check out our articles on testing and error handling patterns and troubleshooting a Next app with ESLint.
Conclusion
Next.js is growing in popularity and usage. But as with any other technology, an application built with Next.js isn’t safe from errors and issues.
By reading up on common errors in Next.js, the causes of these errors, and their respective solutions — as we covered in this article — you can optimize your web app and ensure that it performs as expected.
LogRocket: Full visibility into production Next.js apps
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.
Posted on August 23, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 30, 2024