slew docs/deploy/image-optimization

Image optimization

Every project answers /_slew/image — a resize-and-recompress endpoint for the images already on your site. Ask for a width, get a WebP sized to it, served and cached by the CDN edge:

<img
  src="/_slew/image?url=/photos/hero.jpg&w=1080"
  srcset="/_slew/image?url=/photos/hero.jpg&w=640 640w,
          /_slew/image?url=/photos/hero.jpg&w=1080 1080w,
          /_slew/image?url=/photos/hero.jpg&w=1920 1920w"
  sizes="100vw" alt="…">

There is nothing to enable and nothing to configure. A 1 MB photo typically comes back a tenth the size; the transformation happens off your instance, so a request for an image never wakes a scaled-to-zero server.

Parameters

Parameter Meaning
url Path of the source image on the same site (/photos/hero.jpg). Absolute URLs to other hosts are rejected.
w Target width in pixels. One of 16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840 — the allowlist bounds how many variants one image can produce. Anything else is a 400.
q Optional quality, 1100, default 75. Snapped to the nearest of 25, 50, 75, 90.

Images are never upscaled: a 800px-wide source requested at w=1080 comes back 800px wide.

What gets optimized

JPEG and PNG sources are resized and re-encoded as WebP. Everything else — SVG, GIF, WebP, AVIF, or a url that isn't an image at all — answers with a redirect to the original file, so an <img> pointing at the endpoint always renders something. The same fallback applies to sources over 10 MB and to any transformation error: worst case is your original, unoptimized image.

Caching

Variants cache at the CDN edge effectively forever and are evicted by the same purge a deploy triggers — ship a new version of /photos/hero.jpg and its variants regenerate on first request. Browsers cache variants for an hour. The first request for a given url × w × q pays the transformation (around a second for typical photos, a few seconds for very large ones); every request after that is an edge cache hit.

On password- or org-protected sites the access gate applies to image variants like any other URL, and each unlocked visitor's variants are cached separately at the edge — the same cookie-keyed caching the rest of a protected site uses.

Using it from Next.js

next/image normally optimizes in your server process. Point it at the platform instead with a one-line custom loader — Next's default device sizes are exactly the width allowlist:

// next.config.js
module.exports = {
  output: 'standalone',
  images: { loader: 'custom', loaderFile: './slew-image-loader.js' },
}
// slew-image-loader.js
export default function slewLoader({ src, width, quality }) {
  return `/_slew/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality ?? 75}`
}

Your <Image> components are unchanged; the transformation moves from your instance to the platform. Remote images (an https:// src) still go through Next's own optimizer — the endpoint only serves same-site sources.

Limitations, stated plainly

  • Output is always WebP. Every browser slew supports renders it; AVIF is on the roadmap.
  • Sources must live on the same site. Remote-image allowlists are deliberately not supported yet.
  • Sources over 10 MB serve unoptimized.
  • Available on projects, not on temporary shares.