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

Bug: Random preloads added for images #27910

Closed
muteor opened this issue Jan 9, 2024 · 11 comments · Fixed by #28815
Closed

Bug: Random preloads added for images #27910

muteor opened this issue Jan 9, 2024 · 11 comments · Fixed by #28815
Labels

Comments

@muteor
Copy link

muteor commented Jan 9, 2024

React version: 18.3.0-canary-c29ca23af-20231205

Steps To Reproduce

  1. Render SSR app that has images in its tree using renderToString or renderToPipeableStream

Link to code example:

The current behavior

image

The expected behavior

To not insert preloads randomly.

Note that there is difference between renderToString and renderToPipeableStream, renderToString the preloads appear inside root and renderToPipeableStream they appear in head.

Sorry this is a bit of a question rather than bug report maybe, I have searched around and cannot see any mention of auto-preloading images but I assume there must be something going on that I have missed.

I was able to fix by using 18.3.0-canary-493f72b0a-20230727.

@muteor muteor added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jan 9, 2024
@nickluger
Copy link

nickluger commented Mar 30, 2024

This. Tested with 19.0.0-canary-a73c3450e-20240329.

It would be "kinda" tolerable for the LCP image above fold (featured image or similar), but we have lots of lazy loaded images below fold which contain alternatives like:

<img
  {...imgProps}
/>
<noscript>
  <img
    alt={alt}
    src={noscriptSrc}
  />
</noscript>

For every <img> tag used in noscript, the image is preloaded in <head> which completely defies the purpose of lazy loading.

@muteor
Copy link
Author

muteor commented Apr 5, 2024

I think this is actually now expected behaviour, after digging the source a bit I found f359f9b

Though I could not find any docs/release notes that mention this which is a little odd as now you need to explicitly think about the loading strategy of every img tag you use. For me I was able to "fix" this by making sure all our img tags have either loading=lazy or fetchPriority=low when they are not to be preloaded, this gave me more or less only the LCP images preloaded.

@nickluger For your case you could either add loading=lazy or fetchPriority=low to the fallback images, though depending on your target browsers you could get rid of the noscript parts these days as loading=lazy has pretty wide browser support now.

Would be good to have this documented somewhere and also maybe add this to the react linter.

@nickluger
Copy link

Thanks for the info regarding loading=lazy, that could solve it. 🙏

If this is really a "feature", I have to say, I don't think it should be React's business to put stuff into my <head> automagically…

@eps1lon eps1lon added React 19 and removed Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug labels Apr 5, 2024
@eps1lon
Copy link
Collaborator

eps1lon commented Apr 10, 2024

@muteor Which framework are you using?

@nickluger
Copy link

For me, its Remix.

@eps1lon
Copy link
Collaborator

eps1lon commented Apr 10, 2024

You're probably using their image component that uses ReactDOM.preload. Or they're doing ReactDOM.preload calls some other way. Can you open an issue on their issue tracker first to clarify?

@nickluger
Copy link

I'm not using their image component but <img>, but I will take a look at Remix & React Router code, whether they call this somewhere and open an issue. Interestingly, this behavior is only applied by Remix (or happens for some reason), when upgrading to React canary.

@gnoff
Copy link
Collaborator

gnoff commented Apr 10, 2024

React is adding support for Suspensey "resource". For instance if you render a stylesheet and opt into React's handling of this using "precedence" then commits which contain this stylesheet will wait to commit until the sheet has loaded.

Another area that we're making Suspensey is images. Lazy images indicate that they do not require suspense coordination but by default images will (in the future) block a boundary reveal until the image has loaded and been decoded.

While these features are for client updates there are analogs for SSR. for stylesheets browsers already block paint until stylesheets load in the head. They don't however do this for images. React canaries now preload the first few images rendered if they are not lazy because we want to mimic the coordinated reveal of Suspensey images along with the first paint. It's not a perfect heuristic because it's not actually enforced to be coordinated but it does better align the behavior across suspensy vs lazy images.

Another way you can signal to React that an image is not expected to be part of the first paint is to set the fetchPriority to "low"

@gnoff
Copy link
Collaborator

gnoff commented Apr 10, 2024

For every tag used in noscript, the image is preloaded in which completely defies the purpose of lazy loading.

This is a genuine bug. will land a fix

@nickluger
Copy link

Good to know, thanks for the explanation. 👍

@muteor
Copy link
Author

muteor commented Apr 10, 2024

fwiw no framework used just renderToPipeableStream, in my case I was able to add low priority to a few icon images to get the preload to load reasonable candidates.

Would be good to be able to opt-out of this somehow for a boundary, thinking of the case where you are waiting on component/data to load and don't really care about the image but lazy loading would be inappropriate for an above the fold larger image. Maybe even a way to modify the heuristic via some sort of callback would be useful.

gnoff added a commit that referenced this issue Apr 10, 2024
`<noscript>` scopes should be considered inert from the perspective of
Fizz since we assume they'll only be used in rare and adverse
circumstances. When we added preload support for img tags we did not
include the noscript scope check in the opt-out for preloading. This
change adds it in

fixes: #27910
github-actions bot pushed a commit that referenced this issue Apr 10, 2024
`<noscript>` scopes should be considered inert from the perspective of
Fizz since we assume they'll only be used in rare and adverse
circumstances. When we added preload support for img tags we did not
include the noscript scope check in the opt-out for preloading. This
change adds it in

fixes: #27910

DiffTrain build for [dc6a7e0](dc6a7e0)
rickhanlonii pushed a commit that referenced this issue Apr 11, 2024
`<noscript>` scopes should be considered inert from the perspective of
Fizz since we assume they'll only be used in rare and adverse
circumstances. When we added preload support for img tags we did not
include the noscript scope check in the opt-out for preloading. This
change adds it in

fixes: #27910
rickhanlonii pushed a commit that referenced this issue Apr 11, 2024
`<noscript>` scopes should be considered inert from the perspective of
Fizz since we assume they'll only be used in rare and adverse
circumstances. When we added preload support for img tags we did not
include the noscript scope check in the opt-out for preloading. This
change adds it in

fixes: #27910
EdisonVan pushed a commit to EdisonVan/react that referenced this issue Apr 15, 2024
`<noscript>` scopes should be considered inert from the perspective of
Fizz since we assume they'll only be used in rare and adverse
circumstances. When we added preload support for img tags we did not
include the noscript scope check in the opt-out for preloading. This
change adds it in

fixes: facebook#27910
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants