Image Transforms

Resize, reformat, and optimize images on the fly with URL-based options or named presets. No upload pipeline, no preprocessing — every image in a public bucket can be transformed at the edge and cached globally.

Why this exists

Modern apps need the same image at half a dozen sizes — a hero on desktop, a thumbnail in a list, a square avatar in a comment, an AVIF on phones that support it. Generating all of those at upload time is expensive and inflexible; new screen sizes mean re-processing everything.

Image transforms let you request a sized, formatted, optimized version of any image just by changing the URL. The result is cached at Cloudflare's edge for four hours, so subsequent requests cost nothing.

Two ways to transform

Pick the form that fits your use case.

Direct URLs

Encode options inline. Good for ad-hoc transforms, A/B tests, or one-off requests. The URL is self-describing — anyone reading it can see exactly what was requested.

https://cdn.koolbase.com/cdn-cgi/image/width=400,height=400,fit=cover,format=auto,quality=85/{project_id}/{bucket}/{path}

Named presets

Store a set of options once on the server, reference it by name. Good for fixed sizes you reuse everywhere — thumbnail, hero, card. Change the preset, and every URL using it picks up the new options as the edge cache rolls over.

https://cdn.koolbase.com/p/{project_id}/{preset_name}/{bucket}/{path}

Available options

All options are optional; omit any you don't need. The same set works for direct URLs and presets.

OptionType / valuesDefault
widthinteger, 1–2000original
heightinteger, 1–2000original
formatauto, webp, avif, jpeg, pngoriginal
qualityinteger, 1–10085
fitscale-down, contain, cover, crop, padscale-down
dprinteger, 1–31
gravityauto, center, top, bottom, left, right, top-left, top-right, bottom-left, bottom-rightcenter

format=auto is usually what you want

Cloudflare picks the best format the browser supports — AVIF on Chrome/Edge/Firefox, WebP everywhere else, JPEG as a fallback. You get the smallest payload without writing browser-detection code.

Flutter SDK

The Flutter SDK builds both URL forms for you. Direct transforms with KoolbaseImageTransform:

lib/profile.dart
dart
final url = KoolbaseStorageClient.publicUrl(
  projectId: koolbase.projectId,
  bucket: 'avatars',
  path: 'user_123.png',
  transform: const KoolbaseImageTransform(
    width: 200,
    height: 200,
    fit: KoolbaseImageFit.cover,
    format: KoolbaseImageFormat.auto,
    quality: 85,
  ),
);

Or use a named preset:

lib/profile.dart
dart
final url = KoolbaseStorageClient.publicUrlWithPreset(
  projectId: koolbase.projectId,
  presetName: 'avatar',
  bucket: 'avatars',
  path: 'user_123.png',
);

You can also chain off any KoolbaseObject you already have in hand:

final thumb = obj.publicUrlWithPreset('avatars', 'thumbnail');

React Native SDK

Same two forms, TypeScript-typed.

src/Profile.tsx
tsx
const url = KoolbaseStorage.publicUrl({
  projectId: koolbase.projectId,
  bucket: 'avatars',
  path: 'user_123.png',
  transform: {
    width: 200,
    height: 200,
    fit: 'cover',
    format: 'auto',
    quality: 85,
  },
});
src/Profile.tsx
tsx
const url = KoolbaseStorage.publicUrlWithPreset({
  projectId: koolbase.projectId,
  presetName: 'avatar',
  bucket: 'avatars',
  path: 'user_123.png',
});

Managing presets

Presets are managed in the Koolbase dashboard. Two ways to create one:

From the transform modal

When previewing a transform on any image in a public bucket, click Save as preset in the modal footer, name it, and the current option set is saved server-side. The new preset is immediately usable in URLs and SDKs.

From the Presets page

Open Storage in the dashboard and click the Presets button. You get a list of every preset in the project with create, edit, and delete actions.

Names are immutable

A preset's name is part of the public URL contract — anything you ship with /p/{project_id}/thumbnail/... breaks the day you rename the preset. The dashboard locks the name field on edit; if you need a different name, create a new preset and delete the old one once nothing references it.

Naming rules

Lowercase alphanumeric with _ or -, 1–64 characters, starting with a letter or digit. Names are unique per project.

Caching & pricing

Transformed images are cached at Cloudflare's edge for four hours per unique URL. A request that hits cache costs nothing and serves in milliseconds.

Cloudflare bills unique transformations — each new combination of options against a source image counts once per calendar month, regardless of how many times it's served. The free tier on every Koolbase account includes 5,000 unique transformations per month. Beyond that, $0.50 per 1,000.

What counts as unique

width=400,height=400,format=webp against avatar.png is one unique transformation. The same options against avatar2.png is a second. The same options against avatar.png served 10,000 times is still one.

Limits

  • Transforms only work on public buckets. Private buckets serve signed URLs that can't be transformed at the edge.
  • Source images must be under 20MB and under 100 megapixels (Cloudflare limit).
  • Supported source formats: PNG, JPEG, GIF, WebP, AVIF, SVG.
  • Output width and height are capped at 2000px each.

Troubleshooting

URL returns the original image, untouched

Confirm the bucket is public. Transforms aren't applied to private buckets — if you need transforms on a private image, copy the file to a public bucket first.

Preset URL returns 404

Double-check the project ID and preset name in the URL. The error response from the edge includes the reason — preset not found, invalid project id, or source not found.

Preset edits don't seem to apply

Edge cache is four hours per unique URL. Edits apply to all new requests immediately, but in-flight cached responses serve their old form until expiry. Append a cache-buster query (?v=2) to force a fresh fetch.

CORS errors in the browser

Make sure your app's origin is in the project's Trusted Origins list.