How jekcms handles image performance: native lazy loading, automatic width/height and fetchpriority for the LCP image via an output filter, and automatic AVIF/WebP generation on upload -- and what it deliberately leaves out (no LQIP, no JS lazy-load fallback).
Why Image Performance Matters
Images are almost always the largest contributor to page weight on a content site, and it is easy to end up serving a 4000x3000 original where a 480px-wide thumbnail would do. Getting lazy loading, dimensions, and modern formats right is one of the highest-leverage things you can do for Core Web Vitals on an image-heavy blog, and jekcms automates a surprising amount of it for you.
Native Lazy Loading: The 90% Solution
The loading="lazy" attribute has been supported in Chrome since version 76, Firefox since 75, and Safari since 15.4 — it covers the overwhelming majority of browser traffic today. Adding it to an image tag is trivial:
<img src="/uploads/images/2026/03/post-thumbnail.avif"
alt="Article thumbnail"
width="480" height="300"
loading="lazy">
The browser defers loading the image until it is within a certain distance of the viewport. Images above the fold load immediately; images further down load as the user scrolls toward them.
The critical mistake we see repeatedly: adding loading="lazy" to every image on the page, including the hero image and the first visible content image. That hurts performance because the browser has to evaluate the element's position before deciding to load it, adding unnecessary delay to your LCP element. Never lazy-load images that are visible in the initial viewport.
jekcms does not ship a JavaScript IntersectionObserver fallback for the small share of browsers without native support — native loading="lazy" alone covers the vast majority of real-world traffic, and a JS-based fallback is unnecessary extra parsing weight for almost every visitor. If you specifically need to support very old browsers, you would add that fallback yourself; it is not something jekcms includes out of the box.
Width and Height: Handled Automatically
Cumulative Layout Shift (CLS) is mostly caused by images without explicit dimensions — the browser does not know how much space to reserve until the image downloads. The fix is width and height attributes on every <img> tag.
In jekcms, you mostly do not have to remember this yourself. A global output filter inspects raw <img> tags rendered by theme templates and, when width/height are missing, looks up the image's real dimensions from the media table and fills them in automatically before the page is sent to the browser. It also adds decoding="async" if it is missing. This runs only on images under /uploads/ — your logo, icons, and other theme assets are left untouched.
Aspect-Ratio CSS for Containers
For image containers where you want a consistent shape regardless of the source image's dimensions (post cards, gallery thumbnails, hero banners), the CSS aspect-ratio property combined with object-fit: cover is the cleanest approach:
.card-image {
aspect-ratio: 16 / 10;
overflow: hidden;
background: #f0f0f0;
}
.card-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
One gotcha: aspect-ratio is overridden by an explicit height. If you set both, the height wins — use one or the other.
fetchpriority: The LCP Image Is Promoted Automatically
The fetchpriority attribute lets you hint to the browser which resource should be downloaded first — for the LCP image, this can meaningfully shave time off the Largest Contentful Paint. jekcms does not require you to pass a manual "is this the LCP image?" flag to a helper function. The same output filter that fills in width/height scans the rendered page, finds the first sizeable content image that was not already marked eager, strips any loading="lazy" from it, and adds fetchpriority="high" loading="eager" automatically. Every image after that gets loading="lazy" if it was not already set explicitly. If your theme already sets fetchpriority="high" or loading="eager" on a specific image, jekcms respects that and does not touch it.
AVIF/WebP with Fallback
AVIF typically produces files 30-50% smaller than WebP at equivalent visual quality, and significantly smaller than JPEG. On upload, jekcms's Media class generates AVIF and WebP versions (when your server's GD build supports them) at three sizes — thumbnail (400px), medium (800px), and large (1600px) — alongside the original. The picture() helper wires these into a standard <picture> element:
<picture>
<source srcset="/uploads/images/2026/03/photo-medium.avif" type="image/avif">
<source srcset="/uploads/images/2026/03/photo-medium.webp" type="image/webp">
<img src="/uploads/images/2026/03/photo-medium.jpg"
alt="Description"
width="800" height="500"
loading="lazy">
</picture>
The browser picks the first format it supports: AVIF if available, then WebP, then the original as a last resort. If a server does not have AVIF/WebP encoding available, jekcms falls back to serving the original format rather than failing the upload.
What We Deliberately Left Out
A couple of things you will find in some image-optimization guides are not part of jekcms, on purpose: there is no low-quality image placeholder (LQIP) system — no dominant-color extraction, no BlurHash. If you want a placeholder effect, the simplest approach is a flat neutral background color on the container via CSS, which costs nothing and needs no per-image processing; jekcms just does not generate or store one automatically. Keeping the pipeline to native lazy loading, automatic dimension/priority handling, and AVIF/WebP conversion covers the bulk of the measurable performance gain without adding a JavaScript dependency or extra database columns.
Common Mistakes to Avoid
- Lazy-loading the LCP image. This delays your most important visual element. jekcms's automatic promotion handles this for you in most cases — but if you hardcode
loading="lazy"in a theme template on the hero image, you override that protection. - Serving AVIF without a fallback. A small share of traffic still cannot decode AVIF. Always let jekcms's
picture()helper generate the full fallback chain rather than hand-rolling a single<img>tag with an AVIF source. - Using massive originals for thumbnails. A 400x250 thumbnail does not need a 4000x2500 source — jekcms generates the right size variants automatically on upload; use them instead of the original.
Image optimization is not glamorous work, but between native lazy loading, automatic width/height and fetchpriority handling, and AVIF/WebP conversion, most of it happens without you thinking about it on every post.