


ℹ️ Developed with the help of Claude Code (Anthropic) — disclosed per Flarum Discuss guidelines on AI-assisted extension development.
You open a Flarum discussion, start reading, and a moment later an image somewhere above finishes loading and the whole page lurches downward. You lose your place. That jump is called Cumulative Layout Shift (CLS), and it is one of the most-complained-about UX issues in Flarum threads with images. Google also weighs CLS in its Core Web Vitals ranking signal, so it hurts SEO too.
This extension eliminates CLS for inline post images by reserving the right amount of space before each image starts loading.
While the extension has been entirely built with Claude Code, all the intricate functionality details are designed by me, as is the hardening of the reporting endpoint. The extension has been used actively on my own prod forum and I can attest to the CLS being completely eliminated.
CLS Fix
Stop the page-jump that happens while images load. Built for Flarum 2.0+.
Features
- Zero layout shift on inline post images — every image gets a correctly-sized placeholder before it loads.
- Self-healing dimension cache — the first visitor to a page reports the real image sizes back to your server; everyone after them gets a perfect placeholder.
- No configuration — install, enable, done. No admin settings, no permissions to grant.
- No background workers required — does not use Flarum's queue or scheduler. Works on any host, including shared hosting.
- Works alongside FoF Upload, rich embeds, and other image extensions — images that already declare their size are used as-is. Nothing is re-rendered.
- Cloudflare-friendly — client-side reporting is throttled (one request at a time, 150 ms apart) and self-suppresses on
429 / 503 so it cannot trigger rate limits.
- Hardened reporting endpoint — only authenticated users can write. Server-side defences include a per-user rate limit (60/min), tight dimension and aspect-ratio bounds, first-write protection (an established cache entry can only be flipped by an admin), and an auto-mute that silences any account that repeatedly tries to flip established values. Guests benefit from the cache passively but never write to it.
- Lazy-loaded images — sets
loading="lazy" on inline post images so the browser only fetches images near the viewport.
- Works everywhere posts render — discussions, notifications, mention previews, search snippets, and the composer preview.
- Negative cache — unknown URLs are sentinel-cached for 5 minutes so missing entries do not hammer the database.
How the self-healing cache works
Three sources of truth for an image's natural dimensions, in priority order:
- The post markup itself — if the image was inserted with explicit width/height (e.g. by FoF Upload or a rich-embed extension), those are used as-is.
- The server-side cache — a small table keyed by
sha256(url) mapping known URLs to their natural pixel size, fronted by your cache driver (Redis recommended for large forums; Flarum's default file cache also works fine).
- A 16/9 fallback — only on the very first ever visit to an image URL. The visitor's browser then immediately reports the real dimensions back to the server, and every subsequent visitor hits case 2.
The cache fills itself as people browse. There is no migration step, no preheating script, no cron job. Within a few minutes of normal traffic, an active forum's most-viewed images are all cached and produce zero CLS for everyone.
Installation
composer require ekumanov/flarum-ext-cls-fix
php flarum migrate
php flarum cache:clear
Then enable the extension in the admin panel under Extensions > CLS Fix.
There is nothing to configure.
Updating
composer update ekumanov/flarum-ext-cls-fix
php flarum migrate
php flarum cache:clear
Compatibility
- Flarum 2.0 required (not compatible with Flarum 1.x).
- Plays nicely with FoF Upload, rich embeds, and any other extension that already inserts width/height attributes on images — those images are skipped entirely by the dimension lookup and reporting paths.
Links
Feedback and bug reports welcome!