Skip to content

Commit

Permalink
[Fizz] Responsive images should not be preloaded with link headers
Browse files Browse the repository at this point in the history
link headers are optionally supported for cases where you prefer to send resource loading hints before you're ready to send the body of a request. While many resources can be correctly preloaded from a link header responsive images are currently not supported and end up preloading the default src rather than the correctly sized image. Until responsive images are supported React will not allow these images to preload as headers and will retain them to preload as HTML.
  • Loading branch information
gnoff committed Feb 21, 2025
1 parent 27ba5e8 commit 6c5c8d4
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 7 deletions.
7 changes: 7 additions & 0 deletions packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -2958,6 +2958,9 @@ function pushImg(
if (
headers &&
headers.remainingCapacity > 0 &&
// browsers today don't support preloading responsive images from link headers so we bail out
// if the img has srcset defined
typeof props.srcSet !== 'string' &&
// this is a hueristic similar to capping element preloads to 10 unless explicitly
// fetchPriority="high". We use length here which means it will fit fewer images when
// the urls are long and more when short. arguably byte size is a better hueristic because
Expand Down Expand Up @@ -5703,6 +5706,10 @@ function preload(href: string, as: string, options?: ?PreloadImplOptions) {
if (
headers &&
headers.remainingCapacity > 0 &&
// browsers today don't support preloading responsive images from link headers so we bail out
// if the img has srcset defined
typeof imageSrcSet !== 'string' &&
// We only include high priority images in the link header
fetchPriority === 'high' &&
// Compute the header since we might be able to fit it in the max length
((header = getPreloadAsHeader(href, as, options)),
Expand Down
34 changes: 27 additions & 7 deletions packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3723,23 +3723,43 @@ describe('ReactDOMFizzServer', () => {
});
});

it('encodes img srcset and sizes into preload header params', async () => {
it('omits images from preload headers if they contain srcset and sizes', async () => {
let headers = null;
function onHeaders(x) {
headers = x;
}

function App() {
ReactDOM.preload('presrc', {
ReactDOM.preload('responsive-preload-set-only', {
as: 'image',
fetchPriority: 'high',
imageSrcSet: 'srcset',
});
ReactDOM.preload('responsive-preload', {
as: 'image',
fetchPriority: 'high',
imageSrcSet: 'srcset',
imageSizes: 'sizes',
});
ReactDOM.preload('non-responsive-preload', {
as: 'image',
fetchPriority: 'high',
imageSrcSet: 'presrcset',
imageSizes: 'presizes',
});
return (
<html>
<body>
<img src="src" srcSet="srcset" sizes="sizes" />
<img
src="responsive-img-set-only"
fetchPriority="high"
srcSet="srcset"
/>
<img
src="responsive-img"
fetchPriority="high"
srcSet="srcset"
sizes="sizes"
/>
<img src="non-responsive-img" fetchPriority="high" />
</body>
</html>
);
Expand All @@ -3751,8 +3771,8 @@ describe('ReactDOMFizzServer', () => {

expect(headers).toEqual({
Link: `
<presrc>; rel=preload; as="image"; fetchpriority="high"; imagesrcset="presrcset"; imagesizes="presizes",
<src>; rel=preload; as="image"; imagesrcset="srcset"; imagesizes="sizes"
<non-responsive-preload>; rel=preload; as="image"; fetchpriority="high",
<non-responsive-img>; rel=preload; as="image"; fetchpriority="high"
`
.replaceAll('\n', '')
.trim(),
Expand Down

0 comments on commit 6c5c8d4

Please sign in to comment.