Next.js image loader
An npm package, @image-optimizer/next, providing a next.config image loader that routes next/image through Image Optimizer instead of Next's built-in optimizer. This page documents the planned design; it is not implemented yet.
What Next.js already does for you
This is worth spelling out because it is the part that is easy to misjudge: when a next/image component renders with a sizes prop, Next computes the full list of widths to request from its images.deviceSizes / images.imageSizes config, and generates the srcset itself — including 1x/2x/3x device-pixel-ratio variants when no sizes prop is given. A custom loader does not need to reimplement any of that. Next calls the loader once per width it has already decided it needs:
type LoaderParams = { src: string; width: number; quality?: number }
function imageOptimizerLoader({ src, width, quality }: LoaderParams) {
return `https://image-optimizer-two.vercel.app/img/${stripProtocol(src)}?w=${width}&q=${quality ?? 75}&f=auto`
}That is the entire core of the loader — Next has already solved “what widths do I need.”
Wiring it up
images: {
loader: "custom",
loaderFile: "./image-optimizer-loader.ts",
}The part that is genuinely a gap: explicit per-breakpoint sizes
Next's automatic sizes-driven srcset assumes the image occupies a predictable fraction of the viewport. It does not have a concept of “60×60 on mobile, 300×200 on desktop” — art-directed, not just scaled, per breakpoint. The planned addition on top of the base loader is a small config that lets a project define what counts as each breakpoint and request explicit dimensions for it:
// image-optimizer.config.ts
export default {
breakpoints: {
mobile: { maxWidth: 640, width: 60, height: 60, fit: "crop" },
desktop: { width: 300, height: 200, fit: "cover" },
},
}This is additive — the default loader behavior (driven entirely by Next's own sizes calculation) keeps working without any config at all.
Open questions
- Whether breakpoint config should key off CSS media queries directly instead of a bespoke
maxWidthfield. - How this interacts with
next/image's built-in blur placeholder, which expects the loader to also serve a tiny preview.