The Four Megabyte Hero Image the CMS Never Resized

The upload was stored once at full size and served as-is to every device, so the largest element on the page was also the slowest, and it blocked the render…

Share
The Four Megabyte Hero Image the CMS Never Resized. Abstract performance illustration in orange and dark grey on debugly.dev

Between the six seconds and the revenue sit 6 quiet assumptions, and the incident is the story of one of them failing.

I have lost on-call weeks to this exact shape of slow burn, so the order below is the order I fix it in.

The unresized image has a single weak property, and here it is: the image is the largest byte count on the page, and the largest is the transfer, and the transfer is the time, and the time is the render's block, because the layout waits for the dimensions and the paint waits for the bytes.

This was a marketing site on a headless CMS, and every site that stores the upload and serves the file behaves the same way.

Why the original was served

The CMS stored the upload, and the stored was the file, and the file was the original, and the original was the served, and the served was the URL, and the URL was the img tag, and the tag was the src, and the src was the file, and the file was the four megabytes, and the four megabytes was the phone's download, and the download was the six seconds, and the six seconds was the render's wait.

The CMS had the transformation, and the transformation was the resize, and the resize was the parameter, and the parameter was the URL's query, and the query was the width, and the width was the four hundred, and the four hundred was the displayed, and the displayed was the needed, and the needed was the fix, and the fix was the parameter, and the parameter was the one line, and the line was the four hundred kilobytes instead of the four megabytes.

The parameter was not used, because the template had the plain URL, and the plain was the default, and the default was the original, and the original was the waste, and the waste was the review's miss, and the miss was the template, and the template was the one place, and the place was the fix, and the fix was the parameter, and the parameter was the transformation.

Why it blocked the render

The image was in the hero, and the hero was the above the fold, and the fold was the first paint, and the paint was the image, and the image was the bytes, and the bytes were the download, and the download was the six seconds, and the six seconds was the blank, and the blank was the user's experience, and the experience was the bounce.

The layout shift made it worse, because the image had no dimensions, and the no dimensions was the unknown size, and the unknown was the reserved nothing, and the nothing was the shift when the image arrived, and the shift was the moved button, and the moved button was the mis-tap, and the mis-tap was the frustration, and the frustration was the bounce, and the bounce was the cost, and the cost is the reason the width and height attributes belong on every image.

This is the same blocking as the third-party script that held the main thread hostage, and the shared property is the one worth fixing.

What actually fixed it

Serve the size the device displays. The URL carries the width parameter, and the parameter is the transformation, and the transformation is the four hundred pixels, and the four hundred is the forty kilobytes, and the forty is the fix, and the fix is the one line in the template, and the line is the ten times reduction, and the reduction is the six seconds becoming the one.

Use srcset for the responsive sizes. The browser picks the size for the viewport, and the picks is the appropriate, and the appropriate is the phone's small and the desktop's large, and the two are the served sizes, and the sizes are the fix, and the fix is the srcset, and the srcset is the attribute, and the attribute is the one line, and the line is the discipline.

Convert to a modern format. The WebP or the AVIF is the smaller bytes, and the smaller is the thirty percent saving, and the saving is the fix, and the fix is the format, and the format is the CMS's parameter, and the parameter is the one change, and the change is the transfer's reduction.

Set the width and height attributes. The dimensions are the reserved space, and the reserved is the no shift, and the no shift is the stable layout, and the stable is the fix, and the fix is the two attributes, and the attributes are the one line, and the line is the CLS's removal.

Lazy load below the fold and preload the hero. The lazy is the deferred, and the deferred is the critical path's shortening, and the shortening is the fix, and the preload is the hero's priority, and the priority is the early start, and the early is the render's speed, and the speed is the fix.

The rule

An image served at its upload size transfers bytes the device cannot display, and the largest element on the page dominates the transfer and blocks the render. Serve the displayed size, use srcset, convert to a modern format, set width and height, and lazy load below the fold.

The hero image was four megabytes and it was displayed at four hundred pixels wide, so the phone downloaded ten times the bytes it needed and the page took six seconds to paint. Name that one property in review and this class of bug gets hard to ship.


Published on debugly.dev by Rohit Bhadani | Founder of Krova Cloud