NextJS is a great tool for building React apps. It comes with a lot of flexibility and awesome features like static site generation (with next export
) and image optimization (with the next/image
component).
But there is a problem.
Currently, it is not possible to take advantage of the Image
component when creating a fully-static site with NextJS. Image optimization at build time is not supported out of the box. What is worse, there is little mention of this limitation in the NextJS docs, leading many developers, including myself, to discover it the hard way - by trying to use next/image
with next export
and discovering that it simply does not working.
There is an ongoing discussion in the community about solving the issue. Support for build time image optimization is currently one of the most requested NextJS features. Hopefully, there will be some good news on that front soon.
In the meanwhile, luckily, there is a workaround!
next-optimized-images to the rescue!
next-optimized-images
is a NextJS plugin which reduces image size by optimizing images during build. This means we can use it with next export
and solve the problem of image optimization for our static site.
So let's do it!
Install
First we need to install the plugin and the relevant optimization packages. For our use case, let's say we need to be able to optimize jpeg
, png
and svg
files:
yarn add next-optimized-images imagemin-mozjpeg imagemin-optipng imagemin-svgo
Configure
Next, we need to add the appropriate configuration to our next.config.js
file:
// next.config.js
const withOptimizedImages = require('next-optimized-images')
module.exports = withOptimizedImages({
handleImages: ['jpeg', 'png', 'svg'],
})
TypeScript
If we are using TypeScript and we try to import an image, we will get a complaint similar to:
Cannot find module 'images/logo.svg' or its corresponding type declarations.
Let's fix that by adding the following to types/global.d.ts
declare module '*.svg' {
const content: any
export default content
}
declare module '*.png' {
const content: any
export default content
}
declare module '*.jpeg' {
const content: any
export default content
}
Tests
For our tests to work as expected, we will also need to create a file mock:
// testUtil/fileMock.js
module.exports = {
process() {
return `module.exports = 'test-file-stub'`
},
}
And add it to the jest.config.js
// jest.config.js
module.exports = {
...
moduleNameMapper: {
'.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/testUtil/fileMock.js',
},
}
Usage
We should add our images to the images
directory of our app (unless we have specified otherwise in the next.config.js
file).
We can now import and use images in our components:
// components/Navigation.tsx
import logo from 'images/logo.svg'
const Navigation = () => {
return (
<nav>
<img src={logo} alt="logo" />
</nav>
)
}
export default Navigation
Generate static site
Finally, let's create our static NextJS site with optimized images:
next build && next export
Our project out
directory now contains our static site.
Happy coding! โจ
If you found this article useful consider sponsoring my content!
Follow me on Twitter for more!