Image transforms
Derivatives are produced on the way out of an asset read, cached, and addressed by their canonical key. The pipeline is pure Go, so a deployment needs no system imaging library.
An asset URL accepts three vocabularies. The native grammar is exact; the other two exist so that a
site migrating from Contentful or Strapi keeps its existing <img src> values.
The native grammar
Section titled “The native grammar”Transforms are separated by /, a transform’s parameters by -, and every parameter carries a
one-letter prefix.
?transform=resize-w_800-h_600/format-f_webp-q_80| Transform | Parameters | Effect |
|---|---|---|
resize | w_, h_ | Preserve the aspect ratio. With both edges pinned, cover the box and crop the overflow |
fill | w_, h_ | Force the exact dimensions, abandoning the aspect ratio |
crop | w_, h_, g_ | A box-sized cutout at a gravity |
format | f_, q_ | Re-encode, optionally at a quality |
Gravities are centre, north, south, east, west.
An explicit transform= always wins over the translated vocabularies, so nothing that worked
before they were accepted changed.
A resize is a bound. Asking for more than the source has returns the source. Images are never upscaled, which matches Contentful.
Contentful Images API parameters
Section titled “Contentful Images API parameters”Append them to any asset URL.
/store/default/asset/id/01J9X…?w=800&fm=webp&q=80| Incoming | Translated to | Note |
|---|---|---|
?w=800 | resize-w_800-h_0 | Aspect-preserving. The commonest image URL there is |
?w=800&h=600 | resize-w_800-h_600 | Covers the box and crops the overflow |
?fit=scale&w=800&h=600 | fill-w_800-h_600 | The names cross over: Contentful’s scale is the native fill. Worth checking twice |
?fit=crop&w=400&h=400 | crop-w_400-h_400-g_centre | |
?fit=crop&w=400&h=400&f=top | crop-w_400-h_400-g_north | Focus becomes gravity |
?fm=jpg&q=70 | format-f_jpeg-q_70 | Quality rides the encoder |
Imaging is engaged by w, h, fit, fm or f. A bare ?q=80 is ignored, because q is too
common a parameter name to read as imaging intent, and quality alone names no encoder.
Formats
Section titled “Formats”jpg, jpeg, png, webp, gif, tiff, bmp.
An fm outside that list is refused by name before anything is read, rather than accepted and then
failing further down.
Focus areas
Section titled “Focus areas”f= | Gravity |
|---|---|
center, centre | centre |
top | north |
bottom | south |
left | west |
right | east |
top_left, top_right | north |
bottom_left, bottom_right | south |
The native gravity vocabulary has no corners, so a corner keeps its vertical half, which is the axis that usually carries the subject.
Strapi named formats
Section titled “Strapi named formats”Strapi pre-generates four named sizes at upload. The same names resolve here, derived on demand, as a bound on the longest edge.
/store/default/asset/id/01J9X…?format=thumbnailformat= | Bound |
|---|---|
thumbnail | 245 |
small | 500 |
medium | 750 |
large | 1000 |
A named format is a complete request on its own and takes precedence over the Contentful parameters, since it carries its own dimensions.
Parameters that are refused
Section titled “Parameters that are refused”Each of these returns 400 with a message naming the problem. The alternative would be an image
that looks deliberate and is not, served with a 200, which the caller has no way to detect.
| Request | Response |
|---|---|
fit=pad | Refused. Padding needs a background colour and a compositing step |
fm=avif, fm=heif, fm=heic | Refused. The encoder does not produce them |
f=face, f=faces | Refused. Use an explicit focus area |
q= without fm= | Refused. Quality belongs to an encoder, and no format was named |
fit=crop or fit=thumb without both w and h | Refused. A cutout has no aspect ratio to infer the missing edge from |
fit=scale without both w and h | Refused, for the same reason |
An unknown fit or f value | Refused by name |
Caching
Section titled “Caching”A derivative is content-addressed by its canonical transform key, so two URLs that mean the same thing share one cached derivative. The first request for a given key produces it; later requests serve the stored bytes.
Set Cache-Control per store so a CDN in front of the service caches derivatives too. See
Operations.
Building URLs on the client
Section titled “Building URLs on the client”@kis.ai/clients/content builds the same URLs in JavaScript, so an <img src> can be composed on
the client with no round-trip. It maps the same vocabulary onto the same native grammar, case for
case.