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

bug(next/script): `<Script />` does not execute resource in page context

Open ctjlewis opened this issue 2 years ago • 2 comments

Verify canary release

  • [X] I verified that the issue exists in the latest Next.js canary release

Provide environment information

(N/A)

From Replit:

    Operating System:
      Platform: linux
      Arch: x64
      Version: #21~20.04.1-Ubuntu SMP Fri Aug 5 12:53:07 UTC 2022
    Binaries:
      Node: 12.22.10
      npm: 6.14.16
      Yarn: 1.22.17
      pnpm: N/A
    Relevant packages:
      next: 12.2.5-canary.7
      eslint-config-next: N/A
      react: 17.0.2
      react-dom: 17.0.2

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

A StackOverflow user reported an issue loading a jQuery library with the <Script /> component. Even when the script is correctly placed in public/ and loaded with <Script src="..." />, globalThis.$ is undefined at runtime.

See this repro.

Expected Behavior

<Script src="..." /> should add the referenced code to the runtime bundle.

To test: <Head><Script src="/jquery-3.6.0.min.js" /></Head> should result in useEffect(() => console.log(globalThis.$), []) logging a non-nullish value. See repro.

Link to reproduction

https://replit.com/@ctjlewis/Next-Script-tags

To Reproduce

  1. Attempt to load a minified jQuery bundle in public/ with <Script />
  2. Verify that globalThis.$ is undefined in the page context at runtime

For the Replit, just hit "Run".

ctjlewis avatar Aug 12 '22 15:08 ctjlewis

The jQuery loaded by the <Script /> component will certainly not be available when React hydrates your page by default (If it is available during useEffect, then it is a bug and you should report it to Next.js).

<Script /> component is designed to handle non-essential scripts for you (E.g., Google Analytics is essential for website owners, but not essential for rendering the page). Thus Next.js will never load the script before React hydrates your page, resulting in a better performance and user experience.

In this case, jQuery should be considered an essential script for rendering the page, thus it shouldn't be loaded through <Script />.

I would recommend the following approach:

  • Includes jQuery and bootstrap in your bundle
import $ from 'assets/js/jquery-3.5.1.min.js';
import 'assets/js/bootstrap.bundle.min.js';
  • Use beforeInteractive
// _document.jsx
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'

export default function Document() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
		{/** You can place it everywhere as long as it is inside a custom _document */}
		{/** And Next.js will always inject the script to the <head /> */}
        <Script
          src="assets/js/jquery-3.5.1.min.js"
          strategy="beforeInteractive"
        ></Script>
      </body>
    </Html>
  )
}

SukkaW avatar Aug 14 '22 06:08 SukkaW

I'm having this issue with "Google reCAPTCHA". It loads 9 times out of 10 (on a SSG page).

I correctly render the next/Script tag inside a page within a FormComponent

      <Script src="https://www.google.com/recaptcha/api.js" async defer />

RemyMachado avatar Nov 18 '22 17:11 RemyMachado

I'm having similar issue.

The following code doesn't work at all.

<Head>
  <Script
    async
    data-blockingmode="auto"
    data-cbid={process.env.NEXT_PUBLIC_COOKIE_BOT_TOKEN}
    id="Cookiebot"
    src="https://consent.cookiebot.com/uc.js"
    type="text/javascript"
  />
</Head>

The following variants works. But the first one throws warning messages in the console, and the second loads a bit later.

<Head>
  <script
    async
    data-blockingmode="auto"
    data-cbid={process.env.NEXT_PUBLIC_COOKIE_BOT_TOKEN}
    id="Cookiebot"
    src="https://consent.cookiebot.com/uc.js"
    type="text/javascript"
  ></script>
</Head>
<Head></Head>
<Script
  async
  data-blockingmode="auto"
  data-cbid={process.env.NEXT_PUBLIC_COOKIE_BOT_TOKEN}
  id="Cookiebot"
  src="https://consent.cookiebot.com/uc.js"
  type="text/javascript"
/>

pa4080 avatar Jun 06 '23 07:06 pa4080