Skip to content

Commit 195d1f1

Browse files
authored
fix(next/image): priority in App Router causes double fetch on mobile (#53700)
The root cause is `ReactDOM.preload()` inserts `<link rel="preload">` above the `<meta name="viewport">`. This PR adds a test to prove that upgrading React fixes the issue (see commits). - Depends on facebook/react#27201 - Depends on #53742 - Fixes #53574 - Related #52995
1 parent f0dab3a commit 195d1f1

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
lines changed

test/integration/next-image-new/app-dir/app/static-img/page.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const Page = () => {
2323
return (
2424
<div>
2525
<h1 id="page-header">Static Image</h1>
26-
<Image id="basic-static" src={testImg} placeholder="blur" />
26+
<Image id="basic-static" src={testImg} placeholder="blur" priority />
2727
<TallImage />
2828
<Image
2929
id="defined-width-and-height"

test/integration/next-image-new/app-dir/test/static.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ const runTests = (isDev) => {
6969
)
7070
})
7171
}
72+
it('should have <head> containing <meta name="viewport"> followed by <link rel="preload"> for priority image', async () => {
73+
let metaViewport = { index: 0, attribs: {} as any }
74+
let linkPreload = { index: 0, attribs: {} as any }
75+
$('head')
76+
.children()
77+
.toArray()
78+
.forEach((child, index) => {
79+
const { tagName, attribs } = child
80+
if (tagName === 'meta' && attribs.name === 'viewport') {
81+
metaViewport = { index, attribs }
82+
} else if (
83+
tagName === 'link' &&
84+
attribs.rel === 'preload' &&
85+
attribs.as === 'image'
86+
) {
87+
linkPreload = { index, attribs }
88+
}
89+
})
90+
expect(metaViewport.attribs.content).toContain('width=device-width')
91+
expect(linkPreload.attribs.imagesrcset).toContain(
92+
'%2F_next%2Fstatic%2Fmedia%2Ftest-rect.f323a148.jpg'
93+
)
94+
expect(metaViewport.index).toBeLessThan(linkPreload.index)
95+
})
7296
it('Should automatically provide an image height and width', async () => {
7397
const img = $('#basic-non-static')
7498
expect(img.attr('width')).toBe('400')

test/integration/next-image-new/base-path/pages/static-img.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const Page = () => {
1818
return (
1919
<div>
2020
<h1 id="page-header">Static Image</h1>
21-
<Image id="basic-static" src={testImg} placeholder="blur" />
21+
<Image id="basic-static" src={testImg} placeholder="blur" priority />
2222
<TallImage />
2323
<Image
2424
id="defined-width-and-height"

test/integration/next-image-new/base-path/test/static.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,30 @@ const runTests = (isDev) => {
6464
)
6565
})
6666
}
67+
it('should have <head> containing <meta name="viewport"> followed by <link rel="preload"> for priority image', async () => {
68+
let metaViewport = { index: 0, attribs: {} }
69+
let linkPreload = { index: 0, attribs: {} }
70+
$('head')
71+
.children()
72+
.toArray()
73+
.forEach((child, index) => {
74+
const { tagName, attribs } = child
75+
if (tagName === 'meta' && attribs.name === 'viewport') {
76+
metaViewport = { index, attribs }
77+
} else if (
78+
tagName === 'link' &&
79+
attribs.rel === 'preload' &&
80+
attribs.as === 'image'
81+
) {
82+
linkPreload = { index, attribs }
83+
}
84+
})
85+
expect(metaViewport.attribs.content).toContain('width=device-width')
86+
expect(linkPreload.attribs.imagesrcset).toContain(
87+
'%2F_next%2Fstatic%2Fmedia%2Ftest-rect.f323a148.jpg'
88+
)
89+
expect(metaViewport.index).toBeLessThan(linkPreload.index)
90+
})
6791
it('Should automatically provide an image height and width', async () => {
6892
const img = $('#basic-non-static')
6993
expect(img.attr('width')).toBe('400')

test/integration/next-image-new/default/pages/static-img.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const Page = ({ testImgProp }) => {
2828
return (
2929
<div>
3030
<h1 id="page-header">Static Image</h1>
31-
<Image id="basic-static" src={testImg} placeholder="blur" />
31+
<Image id="basic-static" src={testImg} placeholder="blur" priority />
3232
<Image id="basic-staticprop" src={testImgProp} placeholder="blur" />
3333
<TallImage />
3434
<Image

test/integration/next-image-new/default/test/static.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ const runTests = (isDev) => {
6969
)
7070
})
7171
}
72+
it('should have <head> containing <meta name="viewport"> followed by <link rel="preload"> for priority image', async () => {
73+
let metaViewport = { index: 0, attribs: {} as any }
74+
let linkPreload = { index: 0, attribs: {} as any }
75+
$('head')
76+
.children()
77+
.toArray()
78+
.forEach((child, index) => {
79+
const { tagName, attribs } = child
80+
if (tagName === 'meta' && attribs.name === 'viewport') {
81+
metaViewport = { index, attribs }
82+
} else if (
83+
tagName === 'link' &&
84+
attribs.rel === 'preload' &&
85+
attribs.as === 'image'
86+
) {
87+
linkPreload = { index, attribs }
88+
}
89+
})
90+
expect(metaViewport.attribs.content).toContain('width=device-width')
91+
expect(linkPreload.attribs.imagesrcset).toContain(
92+
'%2F_next%2Fstatic%2Fmedia%2Ftest-rect.f323a148.jpg'
93+
)
94+
expect(metaViewport.index).toBeLessThan(linkPreload.index)
95+
})
7296
it('Should automatically provide an image height and width', async () => {
7397
const img = $('#basic-non-static')
7498
expect(img.attr('width')).toBe('400')

0 commit comments

Comments
 (0)