Skip to content

Commit

Permalink
Make lazy-loading the default for next/image (#18123)
Browse files Browse the repository at this point in the history
Co-authored-by: Steven <steven@ceriously.com>
  • Loading branch information
timneutkens and styfle committed Oct 22, 2020
1 parent 81e667b commit 9fb1e60
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 26 deletions.
59 changes: 35 additions & 24 deletions packages/next/client/image.tsx
Expand Up @@ -142,7 +142,7 @@ export default function Image({
sizes,
unoptimized = false,
priority = false,
lazy = false,
lazy,
className,
quality,
width,
Expand All @@ -156,11 +156,14 @@ export default function Image({
// If priority and lazy are present, log an error and use priority only.
if (priority && lazy) {
if (process.env.NODE_ENV !== 'production') {
console.error(
`Image with src ${src} has both priority and lazy tags. Only one should be used.`
throw new Error(
`Image with src "${src}" has both "priority" and "lazy" properties. Only one should be used.`
)
}
lazy = false
}

if (!priority && typeof lazy === 'undefined') {
lazy = true
}

useEffect(() => {
Expand Down Expand Up @@ -221,10 +224,15 @@ export default function Image({

let divStyle: React.CSSProperties | undefined
let imgStyle: React.CSSProperties | undefined
let wrapperStyle: React.CSSProperties | undefined
if (typeof height === 'number' && typeof width === 'number' && !unsized) {
// <Image src="i.png" width=100 height=100 />
const quotient = height / width
const ratio = isNaN(quotient) ? 1 : quotient * 100
wrapperStyle = {
maxWidth: '100%',
width,
}
divStyle = {
position: 'relative',
paddingBottom: `${ratio}%`,
Expand All @@ -246,36 +254,39 @@ export default function Image({
if (priority) {
// <Image src="i.png" unsized priority />
console.warn(
`Image with src ${src} has both priority and unsized attributes. Only one should be used.`
`Image with src "${src}" has both "priority" and "unsized" properties. Only one should be used.`
)
}
}
} else {
// <Image src="i.png" />
if (process.env.NODE_ENV !== 'production') {
console.error(
`Image with src ${src} must use width and height attributes or unsized attribute.`
throw new Error(
`Image with src "${src}" must use "width" and "height" properties or "unsized" property.`
)
}
}

return (
<div style={divStyle}>
{shouldPreload
? generatePreload({
src,
widths: configSizes,
unoptimized,
sizes,
})
: ''}
<img
{...rest}
{...imgAttributes}
className={className}
sizes={sizes}
ref={thisEl}
style={imgStyle}
/>
<div style={wrapperStyle}>
<div style={divStyle}>
{shouldPreload
? generatePreload({
src,
widths: configSizes,
unoptimized,
sizes,
})
: ''}
<img
{...rest}
{...imgAttributes}
className={className}
sizes={sizes}
ref={thisEl}
style={imgStyle}
/>
</div>
</div>
)
}
Expand Down
4 changes: 4 additions & 0 deletions test/integration/image-component/basic/pages/client-side.js
Expand Up @@ -9,6 +9,7 @@ const ClientSide = () => {
<Image
id="basic-image"
src="foo.jpg"
lazy={false}
width={300}
height={400}
quality={60}
Expand All @@ -17,6 +18,7 @@ const ClientSide = () => {
id="attribute-test"
data-demo="demo-value"
src="bar.jpg"
lazy={false}
width={300}
height={400}
/>
Expand All @@ -25,13 +27,15 @@ const ClientSide = () => {
data-demo="demo-value"
host="secondary"
src="foo2.jpg"
lazy={false}
width={300}
height={400}
/>
<Image
id="unoptimized-image"
unoptimized
src="https://arbitraryurl.com/foo.jpg"
lazy={false}
width={300}
height={400}
/>
Expand Down
4 changes: 4 additions & 0 deletions test/integration/image-component/basic/pages/index.js
Expand Up @@ -9,6 +9,7 @@ const Page = () => {
<Image
id="basic-image"
src="foo.jpg"
lazy={false}
width={300}
height={400}
quality={60}
Expand All @@ -17,6 +18,7 @@ const Page = () => {
id="attribute-test"
data-demo="demo-value"
src="bar.jpg"
lazy={false}
width={300}
height={400}
/>
Expand All @@ -25,13 +27,15 @@ const Page = () => {
data-demo="demo-value"
host="secondary"
src="foo2.jpg"
lazy={false}
width={300}
height={400}
/>
<Image
id="unoptimized-image"
unoptimized
src="https://arbitraryurl.com/foo.jpg"
lazy={false}
width={300}
height={400}
/>
Expand Down
19 changes: 19 additions & 0 deletions test/integration/image-component/default/pages/flex.js
@@ -0,0 +1,19 @@
import React from 'react'
import Image from 'next/image'

const Page = () => {
return (
<div>
<p>Hello World</p>
<Image id="basic-image" src="/test.jpg" width={400} height={400}></Image>
<p id="stubtext">This is the index page</p>
<style jsx>{`
div {
display: flex;
}
`}</style>
</div>
)
}

export default Page
21 changes: 21 additions & 0 deletions test/integration/image-component/default/test/index.test.js
Expand Up @@ -52,6 +52,27 @@ function runTests() {
}
}
})

it('should work when using flexbox', async () => {
let browser
try {
browser = await webdriver(appPort, '/flex')
await check(async () => {
const result = await browser.eval(
`document.getElementById('basic-image').width`
)
if (result === 0) {
throw new Error('Incorrectly loaded image')
}

return 'result-correct'
}, /result-correct/)
} finally {
if (browser) {
await browser.close()
}
}
})
}

describe('Image Component Tests', () => {
Expand Down
Expand Up @@ -45,14 +45,14 @@ describe('TypeScript Image Component', () => {
const html = await renderViaHTTP(appPort, '/valid', {})
expect(html).toMatch(/This is valid usage of the Image component/)
expect(output).not.toMatch(
/must use width and height attributes or unsized attribute/
/must use "width" and "height" properties or "unsized" property/
)
})

it('should print error when invalid Image usage', async () => {
await renderViaHTTP(appPort, '/invalid', {})
expect(output).toMatch(
/must use width and height attributes or unsized attribute/
/must use "width" and "height" properties or "unsized" property/
)
})
})
Expand Down

0 comments on commit 9fb1e60

Please sign in to comment.