Image optimization for static NextJS sites

Image optimization for static NextJS sites

ยท

3 min read

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!

Did you find this article valuable?

Support Where is the mouse? by becoming a sponsor. Any amount is appreciated!