Next.js image loader for Supabase Storage
The @prodkit-cloud/next package wraps next/image with a ProdKit loader that works in App Router server and client components.
SDK coming soon
@prodkit-cloud/next isn't published yet, so the guides below skip the install step. Until it is, build image URLs from your endpoint with URL parameters. They work in any Next.js app with no package:
URL API
// image-loader.ts
"use client"
export default function prodkitLoader({ src, width, quality }: { src: string; width: number; quality?: number }) {
const url = new URL(`https://img.prodkit.cloud/acme/${src.replace(/^\//, "")}`)
url.searchParams.set("width", String(width))
if (quality) url.searchParams.set("quality", String(quality))
return url.toString()
}
// next.config.ts
// images: { loader: "custom", loaderFile: "./image-loader.ts" }
// In a component: src is the path in your bucket
// <Image src="avatars/cory.png" width={128} height={128} alt="Avatar" />Use the App Router component
ProdKitImage is server-component safe and accepts the sizing, priority, className, and sizes props you already pass to next/image.
Example
import { ProdKitImage } from "@prodkit-cloud/next"
export function Cover() {
return (
<ProdKitImage
project="acme"
src="covers/launch.jpg"
width={1280}
height={720}
quality={82}
sizes="(max-width: 768px) 100vw, 1280px"
alt="Launch cover"
/>
)
}Crop to faces and remove backgrounds
Crop and effect props go straight to Cloudflare Images. gravity="face" keeps faces in frame, and removeBackground (beta) returns a transparent cutout.
Example
<ProdKitImage
project="acme"
src={user.avatarPath}
width={96}
height={96}
gravity="face"
placeholder="blur"
alt={user.name}
/>Use the loader directly
For existing next/image components, create a configured loader and pass it to Image. Next still supplies the responsive width and quality values. A function prop can only be passed from a client component, so the file starts with "use client".
Example
"use client"
import Image from "next/image"
import { createProdKitImageLoader } from "@prodkit-cloud/next"
const prodkitLoader = createProdKitImageLoader({ project: "acme" })
export function Avatar() {
return (
<Image
loader={prodkitLoader}
src="avatars/cory.png"
width={128}
height={128}
alt="Avatar"
/>
)
}Set the loader for every image
To send every next/image in the app through ProdKit, including those in server components, export the loader from a loaderFile. next/image is a client component and runs that file, so it starts with "use client" too.
Example
// image-loader.ts
"use client"
import { createProdKitImageLoader } from "@prodkit-cloud/next"
export default createProdKitImageLoader({ project: "acme" })
// next.config.ts
const nextConfig = {
images: { loader: "custom", loaderFile: "./image-loader.ts" },
}
export default nextConfigProdKitImage props
ProdKitImage accepts project, imageBaseUrl, transform props, and the standard next/image display props. Use createProdKitImageLoader when you want to keep existing Image components.
| Name | Description | Values | Default |
|---|---|---|---|
| src | Image path within your project, or a full Supabase Storage URL to rewrite. | string | Required |
| project | Project slug from the dashboard. Can be passed through ProdKitProvider. | string | Required |
| imageBaseUrl | Custom image service base URL for self-hosted or preview environments. | absolute URL | https://img.prodkit.cloud |
| width | Rendered width in CSS pixels. Also caps the generated srcset variants. | 1-4096 | Required |
| height | Rendered height in CSS pixels. | 1-4096 | Auto from aspect ratio |
| quality | Compression quality. | 1-100 | 82 |
| fit | How the image fills the requested dimensions. | scale-down, contain, cover, crop, pad | scale-down |
| format | Output format. auto negotiates AVIF or WebP per browser. | auto, avif, webp, jpeg, png | auto |
| gravity | Where cover and crop fits keep the image: face (detected faces), auto (the most interesting region), a side, or a focal point XxY from 0 to 1. Sets fit=cover unless you choose a fit. | "auto", "face", "top", "bottom", "left", "right", { x, y } | Center |
| zoom | With gravity=face, how tightly to crop around faces, from 0 (loose) to 1 (tight). | 0-1 | None |
| blur | Blur radius, rounded to a standard level. With a small width, makes a lightweight placeholder. | 1-250 | None |
| sharpen | Sharpening strength. 1 suits most downscaled images. | 0-10 | None |
| removeBackground | Cuts out the subject with a transparent background (beta). | boolean | false |
| placeholder | blur shows a tiny blurred version of the image until it loads, generated by ProdKit (no blurDataURL needed). It counts as one of the image's sizes. | blur, empty | empty |