diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 78e263d0aa5bb2d..951d8c60c6d6188 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -245,6 +245,7 @@ function defaultImageLoader(loaderProps: ImageLoaderProps) { function handleLoading( img: HTMLImageElement | null, src: string, + layout: LayoutValue, placeholder: PlaceholderValue, onLoadingComplete?: OnLoadingComplete ) { @@ -267,6 +268,18 @@ function handleLoading( // underlying DOM element because it could be misused. onLoadingComplete({ naturalWidth, naturalHeight }) } + if (process.env.NODE_ENV !== 'production') { + const parent = img.parentElement?.parentElement?.style + if (layout === 'responsive' && parent?.display === 'flex') { + console.warn( + `Image with src "${src}" may not render properly as a child of a flex container. Consider wrapping the image with a div to configure the width.` + ) + } else if (layout === 'fill' && parent?.position !== 'relative') { + console.warn( + `Image with src "${src}" may not render properly with a parent using position:"${parent?.position}". Consider changing the parent style to position:"relative" with a width and height.` + ) + } + } }) } } @@ -612,7 +625,7 @@ export default function Image({ className={className} ref={(img) => { setRef(img) - handleLoading(img, srcString, placeholder, onLoadingComplete) + handleLoading(img, srcString, layout, placeholder, onLoadingComplete) }} style={{ ...imgStyle, ...blurStyle }} /> diff --git a/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js b/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js new file mode 100644 index 000000000000000..b4462487616404c --- /dev/null +++ b/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js @@ -0,0 +1,13 @@ +import React from 'react' +import Image from 'next/image' +import img from '../public/test.jpg' + +const Page = () => { + return ( +
+ +
+ ) +} + +export default Page diff --git a/test/integration/image-component/default/pages/layout-responsive-inside-flex.js b/test/integration/image-component/default/pages/layout-responsive-inside-flex.js new file mode 100644 index 000000000000000..923c9810cb1747b --- /dev/null +++ b/test/integration/image-component/default/pages/layout-responsive-inside-flex.js @@ -0,0 +1,13 @@ +import React from 'react' +import Image from 'next/image' +import img from '../public/test.jpg' + +const Page = () => { + return ( +
+ +
+ ) +} + +export default Page diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js index aff13d076e6ff2c..555a2f134fe4ce1 100644 --- a/test/integration/image-component/default/test/index.test.js +++ b/test/integration/image-component/default/test/index.test.js @@ -582,6 +582,33 @@ function runTests(mode) { ) }) + it('should warn when img with layout=responsive is inside flex container', async () => { + const browser = await webdriver(appPort, '/layout-responsive-inside-flex') + await browser.eval(`document.getElementById("img").scrollIntoView()`) + const warnings = (await browser.log('browser')) + .map((log) => log.message) + .join('\n') + expect(await hasRedbox(browser)).toBe(false) + expect(warnings).toMatch( + /Image with src (.*)jpg(.*) may not render properly as a child of a flex container. Consider wrapping the image with a div to configure the width/gm + ) + }) + + it('should warn when img with layout=fill is inside a container without position relative', async () => { + const browser = await webdriver( + appPort, + '/layout-fill-inside-nonrelative' + ) + await browser.eval(`document.getElementById("img").scrollIntoView()`) + const warnings = (await browser.log('browser')) + .map((log) => log.message) + .join('\n') + expect(await hasRedbox(browser)).toBe(false) + expect(warnings).toMatch( + /Image with src (.*)jpg(.*) may not render properly with a parent using position:\\"static\\". Consider changing the parent style to position:\\"relative\\"/gm + ) + }) + it('should warn when using a very small image with placeholder=blur', async () => { const browser = await webdriver(appPort, '/small-img-import')