The image problem in headless CMS
Image optimization is consistently one of the top contributors to poor Core Web Vitals scores. It's also one of the most tooling-heavy problems in a typical web project. The solutions — Cloudinary, Imgix, Next.js <Image>, <picture> with srcset — each solve the problem but add a dependency, a subscription cost, or a configuration surface.
For a headless CMS, the problem is specific: the CMS gives you image URLs. Serving those images at the right size, in the right format, for the right device is your problem. That usually means a third-party image CDN, a framework-level image component, or a manual transform layer wired up separately.
SleekCMS handles this at the platform level — image transformation via URL query parameters, built into every image asset.
The URL parameter API
Every image uploaded to SleekCMS has a CDN-backed URL. Appending query parameters transforms the image before delivery:
https://cdn.sleekcms.com/images/IMAGE_ID.jpg?w=1200&h=600&fit=cover&fmt=webp&q=85
No API call, no SDK, no configuration — just query parameters on the image URL.
Supported parameters:
| Parameter | Description | Example values |
|---|---|---|
w |
Width in pixels | 400, 1200, 1920 |
h |
Height in pixels | 300, 600, 1080 |
s |
Combined size shorthand (WxH) | 1200x600, 400x300 |
fit |
Crop/resize mode | cover, contain, fill |
fmt |
Output format | webp, jpg, png |
q |
Quality (1–100) | 85, 70, 40 |
dpr |
Device pixel ratio | 2, 3 |
blur |
Gaussian blur radius | 10, 20 |
The EJS helper functions
In SleekCMS templates, you don't typically construct these URLs by hand. The built-in helper functions handle it:
src(image, attr) — returns the transformed URL:
<img src="<%- src(item.cover, '1200x600') %>" alt="<%= item.cover.alt %>">
img(image, attr) — renders a complete <img> element with correct src, alt, width, and height:
<%- img(item.cover, '1200x600') %>
picture(image, attr) — renders a <picture> element with dark/light variant support and responsive source sets:
<%- picture(item.hero_background, { w: 1920, h: 800, fit: 'cover' }) %>
svg(image, attr) — renders an inline SVG for icon fields:
<%- svg(item.logo, { class: 'h-8 w-auto' }) %>
The attr parameter accepts either a "WxH" string for quick sizing or a full object for precise control:
<%- img(item.avatar, { w: 64, h: 64, fit: 'cover', class: 'rounded-full' }) %>
Common patterns
Blog hero image — full width, WebP, high quality:
<%- img(item.cover, { w: 1200, h: 630, fit: 'cover', fmt: 'webp', q: 90 }) %>
Thumbnail in a card grid — small, fast:
<%- img(post.cover, { w: 400, h: 250, fit: 'cover', fmt: 'webp', q: 75 }) %>
Author avatar — small, circular (handled with CSS):
<%- img(item.author_avatar, { w: 64, h: 64, fit: 'cover', class: 'rounded-full' }) %>
OG image for sharing — exact dimensions required by most social platforms:
<% meta({ property: 'og:image', content: src(item.seo.image, '1200x630') }) %>
Blurred placeholder (low-quality image placeholder pattern):
<div style="background-image: url('<%- src(item.cover, { w: 40, blur: 10 }) %>')"
data-src="<%- src(item.cover, '1200x600') %>">
</div>
What this replaces
For most SleekCMS sites, the built-in transform API replaces:
- Cloudinary or Imgix — a third-party image CDN subscription. The transform capabilities overlap significantly for standard web use cases.
- Next.js
<Image>component — if you're using the site builder, there's no Next.js in the stack. Theimg()helper covers the same functionality. - Manual
srcsetattributes — thepicture()helper handles responsive image generation. - PostCSS image plugins — image optimization at build time. The transform URL handles it at request time, per device.
There are cases where a dedicated image CDN is still the right choice: complex focal-point cropping, facial detection, art direction with multiple editorial crops, high-volume media libraries that need advanced asset management. For standard web content — hero images, blog covers, team photos, product thumbnails — the built-in transform API covers the use case.
Core Web Vitals implications
Image optimization affects three of the Core Web Vitals metrics:
Largest Contentful Paint (LCP) — the hero image is often the LCP element. Serving it at the right size (not 4000px wide scaled down in CSS) and in WebP format typically yields the largest single LCP improvement available.
Cumulative Layout Shift (CLS) — the img() helper outputs width and height attributes, giving the browser the aspect ratio before the image loads. This prevents layout shift caused by images loading in without reserved space.
Interaction to Next Paint (INP) — less directly related to images, but serving smaller files reduces the bandwidth contention that delays interaction.
The URL parameter API makes it straightforward to serve the right image for every context — without a separate service, without a build-time step, and without per-framework configuration.