Inlined images in Next.js

Article Logo

The slowest part of loading a web page is almost always user network.

If you have small images like an app logo that you don't want to wait for on network, one tool in your toolbox is to inline these images using base64 encoding.

This is how inlined images look in a browser:

Desired usage

Our goal is to be able to write the following if we want to have an inlined image:

import logo from 'images/app-logo.inline.png'

and the following for the regular URL (notice the .inline. part of the file name):

import logo from 'images/app-logo.png'

It can be later used as usual via:

<Image src={logo}/>

Implementation

How do we do this in Next.js?

We have to add url-loader to package.json and tweak next.config.js.

// next.config.js
module.exports = {
  // ...
  webpack: (config) => {
    // find the built-in loader
    const imageLoaderRule = config.module.rules.find(
      (rule) => rule.loader === 'next-image-loader'
    )
    // make the loader ignore *.inline files
    imageLoaderRule.exclude = /\.inline\.(png|jpg|svg)$/i
    
    // add a new URL loader for *.inline files
    config.module.rules.push({
      test: /\.inline\.(png|jpg|gif)$/i,
      use: [
        {
          loader: 'url-loader'
        }
      ]
    })
    return config
  },
  // ..
}

Types

If we want to avoid Typescript errors we also need to declare types for these inline files

// images/index.d.ts
declare module '*.inline.png' {
  const value: string
  export default value
}
declare module '*.inline.jpg' {
  const value: string
  export default value
}
declare module '*.inline.svg' {
  const value: string
  export default value
}
// tsconfig.json
{
  // ...
  "include": ["images/index.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx"]
}

Popular posts from this blog

HTTP server in Ruby 3 - Fibers & Ractors

Next.js: restrict pages to authenticated users

Migration locks for TypeORM