next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Next/Script Component won't server side render in-line scripts

Open finnhom opened this issue 2 years ago • 3 comments

What version of Next.js are you using?

11.1.2

What version of Node.js are you using?

17.2.0

What browser are you using?

Chrome

What operating system are you using?

macOS

How are you deploying your application?

other

Describe the Bug

The docs for the Next Script component state:

Only the afterInteractive and lazyOnload strategies can be used. The beforeInteractive loading strategy injects the contents of an external script into the initial HTML response. Inline scripts already do this, which is why the beforeInteractive strategy cannot be used with inline scripts.

Yet when I use inline scripts they are not server side rendered. Only by using a script tag in the Head component and dangerouslySetInnerHTML do I get the script SSR

Expected Behavior

The scripts should be in the initial HTML response, i.e. server side rendered

To Reproduce

Add an in-line script component with some content you want in the initial HTML response and then try to access that data via a HTTP client (insomnia or postman or something).

e.g. (doesn't work)

SOME COMPONENT

const jsonLd = {
  '@context': 'http://schema.org',
  '@type': 'NewsArticle'
}

return (
  <Script id="article-jsonld" type="application/ld+json" key="jsonld-article">
    {JSON.stringify(jsonld)}
  </Script>
)

e.g. (does work)

SOME COMPONENT

const jsonLd = {
  '@context': 'http://schema.org',
  '@type': 'NewsArticle'
}

return (
  <Head>
    <script
      id="article-jsonld"
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonld) }}
      key="jsonld-article"
    />
  </Head>
)

finnhom avatar Dec 20 '21 12:12 finnhom

I can reproduce this issue on v12.1. When I try to use the beforeInteractive strategy, the output will be <script ... src(unknown) ...></script>, yet the exact same approach works with afterInteractive. It really annoys me that I get spammed with Do not add <script> tags using next/head but I need the inline script SSR'd, which currently does not work with the new next/script tag.

Fabb111 avatar Mar 14 '22 18:03 Fabb111

I am able to reproduce with the same steps in v12.3.0.

rishi-raj-jain avatar Sep 19 '22 08:09 rishi-raj-jain

strategy="beforeInteractive" solves it.

Here's how I use it:

https://github.com/rishi-raj-jain/rishi.app/blob/master/components/Seo.js#L21

Deployed:

view-source:https://rishi.app/

rishi-raj-jain avatar Sep 20 '22 08:09 rishi-raj-jain

~~The problem seems to be that next/head discards all strategy='beforeInteractive' scripts if they don't have a src defined:~~ https://github.com/vercel/next.js/blob/9ac231a0b4f4e9cbd8fffe1f9155962f5b212df4/packages/next/pages/_document.tsx#L319

~~The children prop is being correctly forwarded (by being spread with the restProps):~~ https://github.com/vercel/next.js/blob/9ac231a0b4f4e9cbd8fffe1f9155962f5b212df4/packages/next/client/script.tsx#L239-L250

~~There seems to be a special handling when appDir is true (layouts?) that appears to do the right thing, but on regular pages it's just silently discarded.~~

Well, I'd missed this, which does handle next/script without src: https://github.com/vercel/next.js/blob/9ac231a0b4f4e9cbd8fffe1f9155962f5b212df4/packages/next/pages/_document.tsx#L561-L565

So this is probably just missing documentation? There's nothing in the next/script documentation that even suggests you can load inline scripts.

Also important that the next/script not be added inside a <Head/> or used from a custom _document; all next/scripts need to be mounted by the time the _document's <Head/> renders.

PS: direct uses of next/script inside _document's <Head/> works; can't have any indirect uses though (e.g. rendering a <MyScript/> component that returns next/script children)

Jessidhia avatar Oct 19 '22 12:10 Jessidhia

It's not clear to me: how should one add inline scripts, then?

Katona avatar Mar 27 '23 08:03 Katona