From 07747c5dbf40464dae62675a7b0cb9d482d58981 Mon Sep 17 00:00:00 2001 From: JamesbbBriz Date: Sat, 12 Sep 2026 01:15:26 +1000 Subject: [PATCH] fix(image): stop forcing object-fit on fill images Next.js builds its fill imgStyle from the legacy objectFit prop (usually undefined) and never sets an inline object-fit, leaving the value to the caller's style or stylesheet. vinext hardcoded objectFit: "cover", so fill images that need contain (logos, diagrams) rendered cropped from both top and bottom even though the docs pattern of passing objectFit through style is the supported escape hatch. Drop the hardcoded value so fill parity holds: the legacy objectFit prop still flows through style, explicit styles still win, and blur placeholder backgrounds keep their own cover sizing independent of this path. --- packages/vinext/src/shims/image.tsx | 4 +++- tests/shims.test.ts | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/vinext/src/shims/image.tsx b/packages/vinext/src/shims/image.tsx index 1e450f4ace..f7892ef387 100644 --- a/packages/vinext/src/shims/image.tsx +++ b/packages/vinext/src/shims/image.tsx @@ -222,12 +222,14 @@ function getFillStyle( style?: React.CSSProperties, backgroundStyle?: React.CSSProperties, ): React.CSSProperties { + // Next.js does not set an inline object-fit for fill images: the style is + // whatever the caller passes (or their CSS), so the legacy objectFit prop + // flows through style and anything else stays under stylesheet control. return { position: "absolute", inset: 0, width: "100%", height: "100%", - objectFit: "cover", ...backgroundStyle, ...style, }; diff --git a/tests/shims.test.ts b/tests/shims.test.ts index c8a7984559..d7aa007def 100644 --- a/tests/shims.test.ts +++ b/tests/shims.test.ts @@ -24529,6 +24529,30 @@ describe("next/image component rendering", () => { expect(html).not.toContain("height="); }); + it("fill images leave object-fit to the caller, matching Next.js", async () => { + const React = await import("react"); + const { renderToStaticMarkup } = await import("react-dom/server"); + const Image = (await import("../packages/vinext/src/shims/image.js")).default; + + const html = renderToStaticMarkup( + React.createElement(Image, { src: "/logo.png", alt: "Logo", fill: true }), + ); + expect(html).toContain("position:absolute"); + // No inline object-fit: the value comes from the caller's style or CSS, + // exactly like next/image. A hardcoded cover crops non-cover images. + expect(html).not.toContain("object-fit"); + + const contained = renderToStaticMarkup( + React.createElement(Image, { + src: "/logo.png", + alt: "Logo", + fill: true, + style: { objectFit: "contain" }, + }), + ); + expect(contained).toContain("object-fit:contain"); + }); + it("renders priority image with fetchpriority=high and loading=eager", async () => { const React = await import("react"); const { renderToStaticMarkup } = await import("react-dom/server");