You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Render SSR app that has images in its tree using renderToString or renderToPipeableStream
Link to code example:
The current behavior
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.
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:
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.
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?
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.
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"
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.
I'm using renderToStaticMarkup to render HTML to a forum. It supports limited HTML, which does not include link tags. Any time I include an img tag, I get an unwanted link tag to go with it.
It is surprising and problematic for me that it renders any markup I didn't explicitly specify, and worse, there's no obvious way to opt-out.
Activity
nickluger commentedon 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:
For every
<img>tag used in noscript, the image is preloaded in<head>which completely defies the purpose of lazy loading.muteor commentedon 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 commentedon Apr 5, 2024
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 commentedon Apr 10, 2024
@muteor Which framework are you using?
nickluger commentedon Apr 10, 2024
For me, its Remix.
eps1lon commentedon Apr 10, 2024
You're probably using their image component that uses
ReactDOM.preload. Or they're doingReactDOM.preloadcalls some other way. Can you open an issue on their issue tracker first to clarify?nickluger commentedon Apr 10, 2024
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 commentedon 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 commentedon Apr 10, 2024
This is a genuine bug. will land a fix
nickluger commentedon Apr 10, 2024
Good to know, thanks for the explanation. 👍
muteor commentedon 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.
[Float] Don't preload images inside `<noscript>` (#28815)
[Float] Don't preload images inside `<noscript>` (#28815)
[Float] Don't preload images inside `<noscript>` (#28815)
[Float] Don't preload images inside `<noscript>` (#28815)
[Float] Don't preload images inside `<noscript>` (react#28815)
imjordanxd commentedon Mar 13, 2025
I recently ran into this issue as well for snapshot tests. It'd be good if this change was documented somewhere 👍
jamesgpearce commentedon Mar 20, 2025
This is very surprising behavior. Worse that it's not documented nor optional.
Especially since if you have already explicitly added the preload link tags to the head yourself and then boom... now you have two of them in the DOM.
No framework; just a raw
renderToStaticMarkupin React 19 will do this.talatkuyuk commentedon Mar 23, 2025
I agree with @jamesgpearce,
I also encountered this issue while testing the parsing of a simple MDX content that only contains an image syntax.
I saw
<link rel="preload" as="image" href="image.png"/>in the result . Then, while digging where does it come from, then I dropped here.I just used a raw
renderToStaticMarkupinreact-dom@19,react-dom/serverin my test. I confirmreact-dom@18doesn't have this behavior.Now, I want to know how to disable it using an option.
errorx666 commentedon May 19, 2025
I'm using
renderToStaticMarkupto render HTML to a forum. It supports limited HTML, which does not includelinktags. Any time I include animgtag, I get an unwantedlinktag to go with it.It is surprising and problematic for me that it renders any markup I didn't explicitly specify, and worse, there's no obvious way to opt-out.
brunolemos commentedon Dec 22, 2025
If you are calling
renderToStaticMarkupoutside a lifecycle method, you can use this instead:If not, you can remove the link tag from the generated html, as a workaround: