vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.13k stars 26.73k forks source link

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

Open finnhom opened 2 years ago

finnhom commented 2 years ago

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>
)
Fabb111 commented 2 years ago

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.

rishi-raj-jain commented 2 years ago

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

rishi-raj-jain commented 2 years ago

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/

Jessidhia commented 1 year ago

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)

Katona commented 1 year ago

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