Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] Preload "suspensey" images #27191

Merged
merged 1 commit into from
Aug 10, 2023
Merged

Conversation

gnoff
Copy link
Collaborator

@gnoff gnoff commented Aug 5, 2023

Eventually we will treat images without loading="lazy" as suspensey meaning we will coordinate the reveal of boundaries when these images have loaded and ideally decoded. As a step in that direction this change prioritizes these images for preloading to ensure the highest chance that they are loaded before boundaries reveal (or initial paint). every img rendered that is non lazy loading will emit a preload just behind fonts.

This change implements a new resource queue for high priority image preloads

There are a number of scenarios where we end up putting a preload in this queue

  1. If you render a non-lazy image and there are fewer than 10 high priority image preloads
  2. if you render a non-lazy image with fetchPriority "high"
  3. if you preload as "image" with fetchPriority "high"

This means that by default we won't overrsaturate this queue with every img rendered on the page but the earlier encountered ones will go first. Essentially this is React's own implementation of fetchPriority="auto".

If however you specify that the fetchPriority is higher then in theory an unlimited number of images can preload in this queue. This gives users some control over queuing while still providing a good default that does not require any opting into

Additionally we use fetchPriority "low" as a signal that an image does not require preloading. This may end up being pointless if not using lazy (which also opts out of preloading) because it might delay initial paint but we'll start with this hueristic and consider changes in the future when we have more information

@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Aug 5, 2023
@react-sizebot
Copy link

react-sizebot commented Aug 5, 2023

Comparing: a20eea2...26bf677

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 164.32 kB 164.32 kB = 51.76 kB 51.76 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 171.74 kB 171.74 kB = 53.97 kB 53.98 kB
facebook-www/ReactDOM-prod.classic.js = 567.12 kB 567.12 kB = 100.09 kB 100.09 kB
facebook-www/ReactDOM-prod.modern.js = 550.92 kB 550.92 kB = 97.25 kB 97.25 kB
facebook-www/ReactDOMServer-prod.modern.js +4.60% 135.60 kB 141.83 kB +2.88% 25.32 kB 26.04 kB
facebook-www/ReactDOMServer-prod.classic.js +4.48% 139.12 kB 145.35 kB +2.78% 26.00 kB 26.73 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +4.39% 142.26 kB 148.51 kB +2.76% 27.00 kB 27.74 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.51% 58.00 kB 59.46 kB +3.53% 17.24 kB 17.85 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.51% 58.03 kB 59.48 kB +3.54% 17.27 kB 17.88 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.51% 58.17 kB 59.63 kB +3.80% 17.52 kB 18.19 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.51% 58.19 kB 59.65 kB +3.81% 17.54 kB 18.21 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.48% 60.12 kB 61.61 kB +3.39% 18.57 kB 19.19 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.47% 60.14 kB 61.63 kB +3.38% 18.59 kB 19.22 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.47% 60.28 kB 61.77 kB +3.31% 18.84 kB 19.46 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.47% 60.31 kB 61.79 kB +3.32% 18.86 kB 19.49 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js +2.33% 62.52 kB 63.98 kB +3.30% 18.96 kB 19.59 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js +2.33% 62.54 kB 64.00 kB +3.30% 18.99 kB 19.61 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.31% 62.78 kB 64.23 kB +3.35% 18.81 kB 19.44 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.31% 62.81 kB 64.26 kB +3.35% 18.83 kB 19.46 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.min.js +2.31% 64.36 kB 65.85 kB +3.18% 19.99 kB 20.63 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.min.js +2.30% 64.39 kB 65.87 kB +3.18% 20.01 kB 20.65 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.30% 64.46 kB 65.95 kB +3.02% 20.01 kB 20.62 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.30% 64.49 kB 65.97 kB +3.02% 20.04 kB 20.64 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
facebook-www/ReactDOMServer-prod.modern.js +4.60% 135.60 kB 141.83 kB +2.88% 25.32 kB 26.04 kB
facebook-www/ReactDOMServer-prod.classic.js +4.48% 139.12 kB 145.35 kB +2.78% 26.00 kB 26.73 kB
facebook-www/ReactDOMServerStreaming-prod.modern.js +4.39% 142.26 kB 148.51 kB +2.76% 27.00 kB 27.74 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.51% 58.00 kB 59.46 kB +3.53% 17.24 kB 17.85 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +2.51% 58.03 kB 59.48 kB +3.54% 17.27 kB 17.88 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.51% 58.17 kB 59.63 kB +3.80% 17.52 kB 18.19 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.production.min.js +2.51% 58.19 kB 59.65 kB +3.81% 17.54 kB 18.21 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.production.min.js +2.48% 60.12 kB 61.61 kB +3.39% 18.57 kB 19.19 kB
oss-stable/react-dom/cjs/react-dom-server.browser.production.min.js +2.47% 60.14 kB 61.63 kB +3.38% 18.59 kB 19.22 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.production.min.js +2.47% 60.28 kB 61.77 kB +3.31% 18.84 kB 19.46 kB
oss-stable/react-dom/umd/react-dom-server.browser.production.min.js +2.47% 60.31 kB 61.79 kB +3.32% 18.86 kB 19.49 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.production.min.js +2.33% 62.52 kB 63.98 kB +3.30% 18.96 kB 19.59 kB
oss-stable/react-dom/cjs/react-dom-server.bun.production.min.js +2.33% 62.54 kB 64.00 kB +3.30% 18.99 kB 19.61 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.31% 62.78 kB 64.23 kB +3.35% 18.81 kB 19.44 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.production.min.js +2.31% 62.81 kB 64.26 kB +3.35% 18.83 kB 19.46 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.production.min.js +2.31% 64.36 kB 65.85 kB +3.18% 19.99 kB 20.63 kB
oss-stable/react-dom/cjs/react-dom-server.edge.production.min.js +2.30% 64.39 kB 65.87 kB +3.18% 20.01 kB 20.65 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.production.min.js +2.30% 64.46 kB 65.95 kB +3.02% 20.01 kB 20.62 kB
oss-stable/react-dom/cjs/react-dom-server.node.production.min.js +2.30% 64.49 kB 65.97 kB +3.02% 20.04 kB 20.64 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +1.31% 60.50 kB 61.30 kB +1.20% 18.37 kB 18.59 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.production.min.js +1.30% 60.67 kB 61.46 kB +1.49% 18.69 kB 18.97 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.production.min.js +1.28% 61.98 kB 62.77 kB +1.18% 19.39 kB 19.62 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.production.min.js +1.28% 62.10 kB 62.89 kB +1.20% 19.45 kB 19.68 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.production.min.js +1.27% 62.31 kB 63.11 kB +1.23% 19.50 kB 19.74 kB
oss-experimental/react-dom/umd/react-dom-server.browser.production.min.js +1.27% 62.26 kB 63.05 kB +1.25% 19.72 kB 19.96 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.production.min.js +1.21% 65.14 kB 65.93 kB +1.05% 20.13 kB 20.34 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.production.min.js +1.21% 65.39 kB 66.18 kB +1.18% 19.97 kB 20.20 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.production.min.js +1.19% 66.45 kB 67.24 kB +1.11% 20.92 kB 21.15 kB
oss-experimental/react-dom/cjs/react-dom-static.node.production.min.js +1.19% 66.52 kB 67.32 kB +1.17% 20.95 kB 21.19 kB
oss-experimental/react-dom/cjs/react-dom-server.node.production.min.js +1.19% 66.55 kB 67.34 kB +1.13% 20.93 kB 21.17 kB
oss-stable-semver/react-dom/cjs/react-dom-server.bun.development.js +0.65% 358.35 kB 360.69 kB +1.15% 79.22 kB 80.14 kB
oss-stable/react-dom/cjs/react-dom-server.bun.development.js +0.65% 358.38 kB 360.72 kB +1.15% 79.25 kB 80.16 kB
oss-stable-semver/react-dom/umd/react-dom-server.browser.development.js +0.65% 378.22 kB 380.67 kB +1.03% 81.08 kB 81.92 kB
oss-stable/react-dom/umd/react-dom-server.browser.development.js +0.65% 378.24 kB 380.70 kB +1.03% 81.11 kB 81.94 kB
oss-stable-semver/react-dom/umd/react-dom-server-legacy.browser.development.js +0.65% 378.48 kB 380.94 kB +1.04% 80.77 kB 81.61 kB
oss-stable/react-dom/umd/react-dom-server-legacy.browser.development.js +0.65% 378.51 kB 380.96 kB +1.04% 80.79 kB 81.64 kB
oss-stable-semver/react-dom/cjs/react-dom-server.browser.development.js +0.65% 361.13 kB 363.47 kB +1.13% 80.14 kB 81.05 kB
oss-stable/react-dom/cjs/react-dom-server.browser.development.js +0.65% 361.16 kB 363.50 kB +1.14% 80.16 kB 81.07 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.65% 361.38 kB 363.72 kB +1.14% 79.84 kB 80.75 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.65% 361.40 kB 363.74 kB +1.14% 79.86 kB 80.78 kB
oss-stable-semver/react-dom/cjs/react-dom-server.edge.development.js +0.65% 361.54 kB 363.88 kB +1.13% 80.26 kB 81.17 kB
oss-stable/react-dom/cjs/react-dom-server.edge.development.js +0.65% 361.57 kB 363.91 kB +1.13% 80.28 kB 81.19 kB
oss-stable-semver/react-dom/cjs/react-dom-server.node.development.js +0.65% 362.61 kB 364.95 kB +1.14% 80.18 kB 81.09 kB
oss-stable/react-dom/cjs/react-dom-server.node.development.js +0.65% 362.64 kB 364.97 kB +1.14% 80.20 kB 81.11 kB
oss-stable-semver/react-dom/cjs/react-dom-server-legacy.node.development.js +0.64% 363.19 kB 365.53 kB +1.14% 80.30 kB 81.21 kB
oss-stable/react-dom/cjs/react-dom-server-legacy.node.development.js +0.64% 363.22 kB 365.56 kB +1.14% 80.32 kB 81.24 kB
facebook-www/ReactDOMServerStreaming-dev.modern.js +0.64% 367.60 kB 369.96 kB +0.71% 79.99 kB 80.56 kB
oss-experimental/react-dom/cjs/react-dom-server.bun.development.js +0.64% 368.07 kB 370.41 kB +1.09% 81.40 kB 82.29 kB
oss-experimental/react-dom/umd/react-dom-server.browser.development.js +0.63% 387.77 kB 390.22 kB +0.69% 83.31 kB 83.88 kB
oss-experimental/react-dom/cjs/react-dom-static.browser.development.js +0.63% 369.57 kB 371.91 kB +1.09% 81.89 kB 82.78 kB
facebook-www/ReactDOMServer-dev.modern.js +0.63% 372.77 kB 375.13 kB +0.70% 81.26 kB 81.82 kB
oss-experimental/react-dom/cjs/react-dom-static.edge.development.js +0.63% 369.98 kB 372.32 kB +1.09% 82.01 kB 82.90 kB
oss-experimental/react-dom/umd/react-dom-server-legacy.browser.development.js +0.63% 388.61 kB 391.07 kB +0.68% 83.20 kB 83.77 kB
oss-experimental/react-dom/cjs/react-dom-server.browser.development.js +0.63% 370.27 kB 372.61 kB +1.09% 82.07 kB 82.96 kB
oss-experimental/react-dom/cjs/react-dom-server.edge.development.js +0.63% 370.68 kB 373.02 kB +1.09% 82.19 kB 83.09 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.browser.development.js +0.63% 371.09 kB 373.43 kB +1.08% 82.00 kB 82.88 kB
oss-experimental/react-dom/cjs/react-dom-static.node.development.js +0.63% 371.71 kB 374.05 kB +1.09% 82.21 kB 83.11 kB
oss-experimental/react-dom/cjs/react-dom-server.node.development.js +0.63% 371.75 kB 374.08 kB +1.09% 82.12 kB 83.01 kB
oss-experimental/react-dom/cjs/react-dom-server-legacy.node.development.js +0.63% 372.91 kB 375.25 kB +1.08% 82.45 kB 83.34 kB
facebook-www/ReactDOMServer-dev.classic.js +0.62% 380.20 kB 382.56 kB +0.69% 82.89 kB 83.45 kB

Generated by 🚫 dangerJS against 26bf677

…ng SSR. We now track whether a rendered img is "suspensey" and emit a corresponding preload behind fonts in priority.
@gnoff gnoff force-pushed the preload-suspensey-imgs branch from 3b44caf to 26bf677 Compare August 9, 2023 19:33
const key = getImagePreloadKey(src, imageSrcSet, imageSizes);
let resource = resources.preloadsMap.get(key);
if (!resource) {
resource = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's extract this stuff too like getPreloadResource(...).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to but I think it needs to go in another PR. there are challenges with this b/c the current implementation is highly optimized to avoid creating props object unless absolutely required and so this can't be tidily implemented in a single function. Also I should do the same thing for stylesheet and other resource types

@gnoff gnoff merged commit f359f9b into facebook:main Aug 10, 2023
@gnoff gnoff deleted the preload-suspensey-imgs branch August 10, 2023 22:40
github-actions bot pushed a commit that referenced this pull request Aug 10, 2023
Eventually we will treat images without `loading="lazy"` as suspensey
meaning we will coordinate the reveal of boundaries when these images
have loaded and ideally decoded. As a step in that direction this change
prioritizes these images for preloading to ensure the highest chance
that they are loaded before boundaries reveal (or initial paint). every
img rendered that is non lazy loading will emit a preload just behind
fonts.

This change implements a new resource queue for high priority image
preloads

There are a number of scenarios where we end up putting a preload in
this queue

1. If you render a non-lazy image and there are fewer than 10 high
priority image preloads
2. if you render a non-lazy image with fetchPriority "high"
3. if you preload as "image" with fetchPriority "high"

This means that by default we won't overrsaturate this queue with every
img rendered on the page but the earlier encountered ones will go first.
Essentially this is React's own implementation of fetchPriority="auto".

If however you specify that the fetchPriority is higher then in theory
an unlimited number of images can preload in this queue. This gives
users some control over queuing while still providing a good default
that does not require any opting into

Additionally we use fetchPriority "low" as a signal that an image does
not require preloading. This may end up being pointless if not using
lazy (which also opts out of preloading) because it might delay initial
paint but we'll start with this hueristic and consider changes in the
future when we have more information

DiffTrain build for [f359f9b](f359f9b)
gnoff added a commit that referenced this pull request Aug 10, 2023
`pushImg` should have been gated by enableFloat

Added in #27191
kodiakhq bot pushed a commit to vercel/next.js that referenced this pull request Aug 12, 2023
@raufdean
Copy link

raufdean commented Aug 31, 2023

Hi seems like the preload only happens for the src/srcset inside an img. If you have a separately defined source tag then the images are not preloaded ? eg in the example below image1.jpg will be preloaded, but image1.webp will not.

<picture>
    <source type="image/webp"
        srcSet={
            "image1.webp 400w"
        }
    />
    <img
        src="image1.jpg"
        width={400}
        height={267}
        fetchpriority="high"
    />
</picture>

vercel/next.js#54799

EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
Eventually we will treat images without `loading="lazy"` as suspensey
meaning we will coordinate the reveal of boundaries when these images
have loaded and ideally decoded. As a step in that direction this change
prioritizes these images for preloading to ensure the highest chance
that they are loaded before boundaries reveal (or initial paint). every
img rendered that is non lazy loading will emit a preload just behind
fonts.

This change implements a new resource queue for high priority image
preloads

There are a number of scenarios where we end up putting a preload in
this queue

1. If you render a non-lazy image and there are fewer than 10 high
priority image preloads
2. if you render a non-lazy image with fetchPriority "high"
3. if you preload as "image" with fetchPriority "high"

This means that by default we won't overrsaturate this queue with every
img rendered on the page but the earlier encountered ones will go first.
Essentially this is React's own implementation of fetchPriority="auto".

If however you specify that the fetchPriority is higher then in theory
an unlimited number of images can preload in this queue. This gives
users some control over queuing while still providing a good default
that does not require any opting into

Additionally we use fetchPriority "low" as a signal that an image does
not require preloading. This may end up being pointless if not using
lazy (which also opts out of preloading) because it might delay initial
paint but we'll start with this hueristic and consider changes in the
future when we have more information
EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
`pushImg` should have been gated by enableFloat

Added in facebook#27191
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
Eventually we will treat images without `loading="lazy"` as suspensey
meaning we will coordinate the reveal of boundaries when these images
have loaded and ideally decoded. As a step in that direction this change
prioritizes these images for preloading to ensure the highest chance
that they are loaded before boundaries reveal (or initial paint). every
img rendered that is non lazy loading will emit a preload just behind
fonts.

This change implements a new resource queue for high priority image
preloads

There are a number of scenarios where we end up putting a preload in
this queue

1. If you render a non-lazy image and there are fewer than 10 high
priority image preloads
2. if you render a non-lazy image with fetchPriority "high"
3. if you preload as "image" with fetchPriority "high"

This means that by default we won't overrsaturate this queue with every
img rendered on the page but the earlier encountered ones will go first.
Essentially this is React's own implementation of fetchPriority="auto".

If however you specify that the fetchPriority is higher then in theory
an unlimited number of images can preload in this queue. This gives
users some control over queuing while still providing a good default
that does not require any opting into

Additionally we use fetchPriority "low" as a signal that an image does
not require preloading. This may end up being pointless if not using
lazy (which also opts out of preloading) because it might delay initial
paint but we'll start with this hueristic and consider changes in the
future when we have more information

DiffTrain build for commit f359f9b.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
`pushImg` should have been gated by enableFloat

Added in #27191

DiffTrain build for commit 533fc28.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants