A repository of bitesize articles, tips & tricks
(in both English and French) curated by Mirego’s team.

Webpack inline loaders and Gatsby

In two Gatsby websites we manage (open.mirego.com and til.mirego.com), we had the same snippet of code:

import Favicon from '../images/favicon.png';
import OpenGraphImage from '../images/social.png';

<link rel="shortcut icon" href={Favicon} type="image/png" />
<meta property="og:image" content={OpenGraphImage} />

However, the result on both websites was different!

<link
  rel="shortcut icon"
  type="image/png"
  href="data:image/png;base64,iVBORw0KGgoAAAAN…"
/>
<meta
  property="og:image"
  content="/static/social-c6e8d5b876fd55275db24a1e066ef2d8.png"
/>
<link
  rel="shortcut icon"
  type="image/png"
  href="data:image/png;base64,iVBORw0KGgoAAAAN…"
/>
<meta
  property="og:image"
  content="data:image/png;base64,iVBORw0KGgoAAAANABCDEFG…"
/>

Unlike the favicon, we don’t want our og:image tag to reference a base64-encoded data:… image — we want it to reference an actual URL. And the only difference between the two sites are that the social.png file in one was 6kb and the other one was 11kb 🤔.

After digging into Gatsby’s source code, I found out that Gatsby’s url-loader configuration defines a 10000 size limit before falling back to file-loader.

At first, I thought I wanted to change the option site-wide to fix the problem. But instead I started to look into Webpack inline loaders (which I didn’t know existed, thanks @Madumo!) to fix my specific usecase.

It now works perfectly!

But with two caveats:

  1. Using Webpack’s file-loader as an inline loader doesn’t inherit Gatsby’s configuration (in particular, the file path pattern), so we need to pass it explicitely to remain consistent with our other bundled static files.
  2. We have to use the ! prefix to make sure only our inline loader is used and it’s not just called after the default one (url-loader).

So, to summarize, here’s the output for a social.png file that’s under 10000 bytes:

import OpenGraphImage from '../images/social.png';
// => "data:image/png;base64,iVBORw0KGgoAAAAN…"

import OpenGraphImage from 'file-loader!../images/social.png';
// => "e2f3b840bf4061c99918ba05a4398e56.png" (a text file that contains `module.exports = "data:image/png;base64,iVBORw0KGgoAAAAN…"`)

import OpenGraphImage from '!file-loader!../images/social.png';
// => "e2f3b840bf4061c99918ba05a4398e56.png" (an actual PNG file)

import OpenGraphImage from '!file-loader?{"name":"static/[name]-[hash].[ext]"}!../images/social.png';
// => "/static/social-e2f3b840bf4061c99918ba05a4398e56.png"

Powerful stuff! 💪