Eleventy's Image Plugin Disk Caching Approach For HTML Transform Method

Murtuzaali Surti
Murtuzaali Surti

• 4 min read

Updated On

Table of Contents

I use the HTML transform method of the @11ty/eleventy-img plugin to post process any img or picture tags in my html. I find this method easy and universal. In this guide, I'll walk you through an approach of utilizing disk cache while using the HTML transform method of the eleventy image plugin.

TLDR

  • Tell the plugin to store the optimized images in the .cache folder which can be preserved between builds, instead of the build output directory which you might want to clear before each build.
eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
   formats: ["avif", "webp"],
   outputDir: ".cache/@11ty/img/",
   urlPath: "/img/built/", // relative to the build output dir (<img> src)
});
  • Copy the built images from the .cache folder to the build output after eleventy has finished processing. Here, public is the build output directory.
eleventyConfig.on("eleventy.after", () => {
    cpSync(
        ".cache/@11ty/img/",
        "public/img/built/", // "public" is the build output dir
        { recursive: true },
    );
});

Eleventy Image Plugin Cache

Eleventy's image plugin has primarily two caching options — in-memory cache and disk cache.

In-Memory Cache

The in-memory cache (as well as disk cache) is controlled by the useCache config option which is enabled by default. While in watch/serve mode, identical requests to the same source and with the same config options will get cached and subsequent requests will return that cached response.

This is a temporary cache which only persists during the current running process. If the process is killed (i.e. 11ty stops running), the cache is gone.

Also, while using the HTML transform method and doing local development, the images will not be optimized beforehand — instead, they will be optimized on demand/request.

It means if you open a page containing a single image, no other image except that single image will be optimized until it is requested. The transformOnRequest option governs this behavior and is enabled by default for 11ty's serve mode.

Disk Cache

Disk cache is a persistent cache which allows me to not re-optimize every single image at every single build. However, there are some caveats which I ran into while using it with the HTML transform method and which you should be aware of:

  • HTML transform method co-locates optimized images by default, so your images don't go to a single directory.
  • Disk cache requires you to check-in and persist images in your output directory across builds. This doesn't work if you clean your output directory before every new build.
  • Even if the .cache folder is preserved by your hosting provider, if you clean your output directory between builds, the images will be re-fetched and re-optimized.

I typically clean the output directory before generating a new build and that's why I needed a workaround to ensure the existing images are cached and persistent across builds while using the HTML transform method of the eleventy image plugin.

So, I posted my observations on Mastodon and, thankfully, got a response from @zachleat regarding a potential workaround.

Approach

Firstly, instead of storing the images in the 11ty output directory, you tell the eleventy image plugin to optimize and store the images directly to the .cache folder which you can preserve across builds.

import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";

/** @param {(import("@11ty/eleventy").UserConfig)} eleventyConfig */
export default function (eleventyConfig) {
    // ...
    const persistentImageOutputDir = ".cache/@11ty/img/";
    const pathRelativeToBuildOutputDir = "/img/built/";

    eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
        formats: ["avif", "webp"],
        outputDir: persistentImageOutputDir,
        urlPath: pathRelativeToBuildOutputDir,
    });

    // ...
}

It's important to specify the URL path (<img> src path) as you will be copying the optimized images from the .cache folder to the build output directory.

Next, you can copy the optimized images from the .cache folder to a directory in the build output directory after they have been optimized using the eleventy.after event.

import { cpSync } from "node:fs";
import { eleventyImageTransformPlugin } from "@11ty/eleventy-img";

/** @param {(import("@11ty/eleventy").UserConfig)} eleventyConfig */
export default function (eleventyConfig) {
    // ...
    const persistentImageOutputDir = ".cache/@11ty/img/";
    const pathRelativeToBuildOutputDir = "/img/built/";

    eleventyConfig.addPlugin(eleventyImageTransformPlugin, {
        formats: ["avif", "webp"],
        outputDir: persistentImageOutputDir,
        urlPath: pathRelativeToBuildOutputDir,
    });

    eleventyConfig.on("eleventy.after", () => {
        cpSync(
            persistentImageOutputDir,
            `public${pathRelativeToBuildOutputDir}`, // "public" is the build output dir
            { recursive: true },
        );
    });

    // ...
}

That's pretty much it. Now, it doesn't matter if you clear your build output directory before every build, the only thing you need to do is preserve the .cache folder between builds and that is already configured by default by some of the hosting providers.

I use Vercel and they provide a zero configuration support for preserving the .cache folder — I just have to select 11ty as a preset/framework. It works until and unless the hosting provider invalidates and discards the .cache folder for some reason. With Vercel, I have seen that it gets discarded when you do package upgrades (package-lock.json gets updated).

There is an open issue on the eleventy-image github repo which lists the exact workaround. Hop in there if you face any issues.


Claude 3.7 Sonnet, OpenAI's GPT 4.5 and Microsoft's Quantum Chip

Previous