react: Ignore

React 16.0.0 with SSR & lazysizes 4.0.1.

Trying to use the “the noscript pattern” to lazy load images with lazysizes but I’m seeing this:

Warning: Expected server HTML to contain a matching <img> in <noscript>.

Image component render method:

render () {
    const { cdn, url, width, height } = this.props

    if (!url) return null

    const noScriptImgProps = {
      src: `${cdn}${url}`,
      className: classNames('product-image'),
      width,
      height
    }

    const imgProps = {
      'data-src': `${cdn}${url}`,
      className: classNames('product-image', 'lazyload'),
      width,
      height
    }

    return (
      <span>
        <noscript>
          <img {...noScriptImgProps} />
        </noscript>
        <img {...imgProps} />
      </span>
    )
  }

Does React have an issue with noscript tags…?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 2
  • Comments: 44 (10 by maintainers)

Commits related to this issue

Most upvoted comments

OK, I understand why this happens now. But it’s not clear to me what’s the best fix.

As a workaround you can replace this:

        <noscript>
          <img src='https://placeimg.com/300/200/animals' width='300' height='200' />
        </noscript>

with this:

        <noscript
          dangerouslySetInnerHTML={{
            __html: "<img src='https://placeimg.com/300/200/animals' width='300' height='200' />"
          }}
        />

I verified it works and doesn’t warn.

The reason you see the mismatch is because the browser is parsing the noscript content as text, but React attempts to hydrate it as a real tree. Maybe React shouldn’t attempt to hydrate anything in noscript.

I tried with an iframe, the below approach doesn’t work.

<noscript
          dangerouslySetInnerHTML={{
            __html: "<iframe src='https://www.googletagmanager.com/ns.html?id=1234'></iframe>"
          }}
        />

image

It’s still coming as a string. For image,p etc have no problem. (Please forget the mismatch between src and all. Not working in both scenarios). And I found some other issues too if I used string literals. <iframe src="https://www.googletagmanager.com/ns.html?id=${val}></iframe>

Hey @muhammadInam! Since I am unsure if the direction of the fix I suggested is good I’m waiting for feedback before doing a PR, maybe @gaearon who’s been active in the thread could chip in? (Only my last post is relevant in regards to a fix, rest is just background and context around how we use noscript and a bit wall-of-texty, sorry…)

Basically I think my questions are:

  • Is shouldSetTextContent too “late” a place to solve this optimally since the reconciler already knows about the children, props etc?
  • Saying that <noscript> only contains text is kind of true on the client and solves the problem, but does it have any bad consequences/side-effects?

If this is an obviously bad direction I’d be happy to let it go and let you work on it @muhammadInam, if it’s not terrible at first glance I’d be happy to open a PR and continue discussions there. 😃

This or next week. But I’ve said this a few times in the past…

After update to react 16.5.0 the warning is gone and <noscript> content rendered even the child is not string. Thanks for the fix guys.

Warning: Prop dangerouslySetInnerHTML did not match. Server: "&lt;p&gt;.&lt;/p&gt;" Client: "<p>.</p>"

This is a bug in 16.0.0: https://github.com/facebook/react/issues/11103. It was fixed in master via https://github.com/facebook/react/pull/11119 and will be released in the next update.

By adding a case to the SSR fixture I verified today that:

<noscript dangerouslySetInnerHTML={{ __html: "<img src='...' /> }} />

still works in master, but that:

<noscript>
  <img src='...' />
</noscript>

is still broken in the way described, that is, it unnecessarily downloads the image.

I also verified that the solution I proposed in the comment above (adding type === 'noscript' to shouldSetTextContent) does indeed make the second case work as expected.

I would still be happy to send a PR for this, including the updated fixture. I might just go ahead and do that this weekend to have a better place to discuss the proposed solution. 😃

I am also using the noscript tag in a project in a similar fashion as @stephen-last. I have a long page of images, most of which are lazy loaded. For the ones that are lazy loaded, there is a coresponding noscript tag containing an image tag with the target source, this is the Google recommended way to provide SEO content for lazy loaded images. When React Hydration occurs, it looks like the img tag within the noscript tag gets ‘executed’, and the image is getting downloaded in the browser. If I look at the network tab in Chrome, I can see all of the noscript wrapped images have been requested. If I block React Hydration, and only rely on the SSR HTML, the noscript wrapped images do not get downloaded. React components that contain noscript tags should not attach the child contents as actual DOM nodes.

Sounds good. I haven’t looked into this again yet. A proof of concept would help the conversation.

Sounds good, @clemmy good luck with the studies!

@gaearon I will be prioritizing studies for the next few months. Please feel free to assign the task to someone else. 😄

@sebmarkbage When I use <noscript>{staticMarkup}</noscript> I get:

Warning: Text content did not match. Server: "&lt;img src=&quot;...&quot; class=&quot;product-image&quot; width=&quot;50&quot;/&gt;" Client: "<img src="..." class="product-image" width="50"/>"

Using <noscript dangerouslySetInnerHTML={{ __html: staticMarkup }} /> produces no warnings with 16.1.0-beta.

On the general <noscript> issue, after thinking about it over the weekend, I agree with @sebmarkbage. On the server React should allow any valid HTML or text. On the client React should ignore the contents completely, as that content only exists for when React doesn’t - in that way React has nothing to do with contents of <noscript>.

I can’t think of a use case for React, or any script, to use the content of <noscript> tags on the client.

I guess in browsers where scripts are disabled this wouldn’t be parsed as text content. So it’s legit to put elements in there.

The issue is what happens on the client. Maybe client rendering should just ignore the content completely (and ignore hydration errors)?

I don’t quite fully understand the use cases where the content is used during a script enabled render. Are there such use cases?