react-lazy-load-image-component icon indicating copy to clipboard operation
react-lazy-load-image-component copied to clipboard

SSR, image not rendered from server

Open Gorbus opened this issue 3 years ago • 6 comments

Bug description The lazy loading is working perfectly on client side but none of my image are rendered from the server on the original loading SSR. Is there a special config to change in order for the image to be rendered as SSR? Stack is Meteor/React/Apllo/Styled Component

<StyledLazyLoadImage
  alt={`product image ${name}`}
  scrollPosition={scrollPosition}
  src={imgUrl200 ? imgUrl200 : imgUrl}
/>
const StyledLazyLoadImage = styled(LazyLoadImage)`
  margin: 0.5rem;
  max-width: 18rem;
  max-height: 22rem;
`;

Also tried without the scrollPosition but result is the same.

Expected behavior Was expecting the image to be rendered on server side.

Screenshots image

Technical details:

  • Package version: 1.5.1
  • Server Side Rendering? Yes
  • Device: Desktop
  • Operating System: MacOs
  • Browser: Chrome

Gorbus avatar Jan 16 '21 06:01 Gorbus

Have you solved it?

foubei avatar Mar 02 '21 02:03 foubei

Facing same issue.

jigar775 avatar Mar 05 '21 18:03 jigar775

I can confirm, this issue is still here. :/

petr-glaser-deltatre avatar Jan 13 '22 16:01 petr-glaser-deltatre

Found the issue. The library is SSR friendly in a way it does not crash. So the flow on SSR with disabled JS is this:

  • Start render on server
  • PlaceholderWithoutTracking is rendered and !supportsObserver is true
  • updateVisibility is called, but typeof window === 'undefined' is true, so false is returned
  • SSR renders placeholder, default placeholder is <span>

Solution is to use placeholder={<img src={src} loading="lazy" />} which will keep the image rendered even on SSR with JS disabled.

petr-glaser-deltatre avatar Jan 13 '22 17:01 petr-glaser-deltatre

Found the issue. The library is SSR friendly in a way it does not crash. So the flow on SSR with disabled JS is this:

  • Start render on server
  • PlaceholderWithoutTracking is rendered and !supportsObserver is true
  • updateVisibility is called, but typeof window === 'undefined' is true, so false is returned
  • SSR renders placeholder, default placeholder is <span>

Solution is to use placeholder={<img src={src} loading="lazy" />} which will keep the image rendered even on SSR with JS disabled.

This way seems to be reasonable and workable and in the meantime we can still use placeholderSrc in parallel as the placeholderSrc will be used as a CSS background image property of the <span/> wrapper and the placeholder elements we explicitly set is a separate <img/> tag inside the wrapper. Btw a quick reminder that don't forget to set srcSet to the <img/> tag for the placeholder property if the original image is supposed to be assigned with it, otherwise your browser will waste network bandwidth to load unnecessary image files according to the src at first and then load the next correct resolution version according to the srcSet on the real <img/> tag after the website is mounted.

ItsRyanWu avatar Jul 06 '22 09:07 ItsRyanWu

Found the issue. The library is SSR friendly in a way it does not crash. So the flow on SSR with disabled JS is this:

  • Start render on server
  • PlaceholderWithoutTracking is rendered and !supportsObserver is true
  • updateVisibility is called, but typeof window === 'undefined' is true, so false is returned
  • SSR renders placeholder, default placeholder is <span>

Solution is to use placeholder={<img src={src} loading="lazy" />} which will keep the image rendered even on SSR with JS disabled.

I think an even better solution is we could wrap the <img/> inside the placeholder property with <noscript> tag, so it's will become only readable to the search engine and won't be executed by browsers in normal cases. I personally think it's a good way to improve performance when it's being parsed by browsers, as the placeholder is not really needed if you already set placeholderSrc property. So the final answer from me would be like this:

placeholder={
  <span>
    <noscript>
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img src={src} srcSet={srcSet} sizes={sizes} alt="placeholder" />
    </noscript>
  </span>
}

To be noticed that a <span/> or other non <noscript/> tags is required here to be a root node to prevent <LazyLoadImage/> from being failed to mount the real image element.

ItsRyanWu avatar Jul 06 '22 09:07 ItsRyanWu