From 007c7a40e4e62e9034e7565e44618403125d5a52 Mon Sep 17 00:00:00 2001 From: Tyler Lawson Date: Mon, 1 Jun 2026 08:23:53 -0700 Subject: [PATCH] fix: use resize=contain in supabase image loader to prevent crop (#301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crop in #286 was in the image *serving* path, not upload. The next/image custom loader (supabase-image-loader.js) hit Supabase's render/image endpoint without a resize param, so it defaulted to resize=cover. Because next/image only passes `width` to a custom loader (never height), imgproxy filled the missing height with the source height and cropped the width to fit the box — chopping the right side off large images. Verified against the reported image on prod (5712x4284) and locally: width=3840 -> cropped (full source height kept) width=2500 -> still cropped (clamping width alone does not help) width=3840&resize=contain -> correct aspect, no crop Co-authored-by: Claude Opus 4.8 (1M context) --- supabase/supabase-image-loader.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/supabase/supabase-image-loader.js b/supabase/supabase-image-loader.js index fcf6f796..50152558 100644 --- a/supabase/supabase-image-loader.js +++ b/supabase/supabase-image-loader.js @@ -1,4 +1,8 @@ export default function supabaseLoader({ src, width, quality }) { const path = src.startsWith("/") ? src.slice(1) : src; - return `${process.env.NEXT_PUBLIC_SUPABASE_API_URL}/storage/v1/render/image/public/${path}?width=${width}&quality=${quality || 75}`; + // resize=contain scales proportionally to fit `width`. Without it Supabase + // defaults to resize=cover, and since next/image only passes width (no + // height), imgproxy fills the missing height with the source height and crops + // the width to fit — chopping the right side off large images. See #286. + return `${process.env.NEXT_PUBLIC_SUPABASE_API_URL}/storage/v1/render/image/public/${path}?width=${width}&quality=${quality || 75}&resize=contain`; } -- 2.51.2