How to Lazy Load Images with an Edge Worker

Lazy loading your images is a great way to increase performance and other Google Lighthouse metrics. Fortunately, there's now a simple native attribute you can add your your <img> tags that will lazy load them without any extra code.

<img loading="lazy" src="" />

Of course, this is easier said than done if you're using a WYSIWYG editor or legacy platform that doesn't support this attribute. Fortunately, edge functions can help us here. Only a few CDN providers are offering edge functions. For this example we'll use Cloudflare Workers.

If you're not familiar with them, edge functions are basically a bit of code that runs right before the content gets sent back to the user. So after your server is done doing whatever it's doing, the edge function can intercept the result and do whatever you want to it. You can use it to alter the code sent back, proxy another site entirely, and many other things.

Cloudflare Workers have a feature that allow you to transform HTML. With this we can make small tweaks on the edge that might be harder to do, or sometimes impossible, on the server.

Here's the code to add the native lazy loading feature to all images:

// Image rewriter that adds loading="lazy" to <img> tags that don't already have it
class ImageRewriter {
  element(element) {
    const attribute = element.getAttribute(`loading`)
    if (!attribute) {
      element.setAttribute(`loading`, `lazy`)

// Listens for incoming page requests
addEventListener(`fetch`, (event) => {

// An HTML rewriter that intercepts and rewrites <img> tags
const rewriter = new HTMLRewriter().on(`img`, new ImageRewriter())

// Fetches content from origin and pass through transformer
async function handleRequest(req) {
  const res = await fetch(req)
  return rewriter.transform(res)

It's simple, but can have a major performance impact across your entire website. You can take this much further if you wanted and add custom attributes to ignore lazy loading on some images, or have a fallback for browsers that don't support the native lazy loading attribute. But this is good enough for most use cases.