fix(theme): use next/script beforeInteractive — <script> in RSC was breaking the flight client
Next.js 16 + React 19 reject <script> tags rendered from React components: 'Encountered a script tag while rendering React component. Scripts inside React components are never executed when rendering on the client.' The error fires during RSC rendering for every page that uses the root layout, which corrupts Turbopack's flight stream and surfaces as 'chunk.reason.enqueueModel is not a function' on every navigation (vercel/next.js#92362). The dev-overlay recommendation to use <template> is misleading — that won't execute a script. The supported App Router pattern is <Script strategy="beforeInteractive"> from next/script, which inlines the script in <head> before hydration. Theme behaviour is unchanged.
This commit is contained in:
parent
5c26f39f2c
commit
950c3fb8ee
1 changed files with 18 additions and 5 deletions
|
|
@ -1,10 +1,19 @@
|
|||
import Script from "next/script";
|
||||
|
||||
/**
|
||||
* Applies the stored theme before first paint.
|
||||
*
|
||||
* This has to be a blocking inline script in <head>. A React effect runs after
|
||||
* hydration, by which point the browser has already painted the default theme
|
||||
* and the user sees a white flash on every navigation to a dark board. There
|
||||
* is no way to fix that from inside React.
|
||||
* Has to be inline and before hydration. A React effect runs after hydration,
|
||||
* by which point the browser has already painted the default theme and the
|
||||
* user sees a white flash on every navigation to a dark board.
|
||||
*
|
||||
* Renders via `next/script` with `beforeInteractive` rather than a plain
|
||||
* `<script>` element. Next.js 16 + React 19 reject `<script>` inside React
|
||||
* components because the React renderer does not execute them, and the
|
||||
* resulting error during RSC streaming corrupts Turbopack's flight client
|
||||
* (the `chunk.reason.enqueueModel is not a function` crash in
|
||||
* vercel/next.js#92362). `beforeInteractive` is the supported way to inject
|
||||
* a blocking inline script in the App Router.
|
||||
*/
|
||||
const SCRIPT = `
|
||||
(function () {
|
||||
|
|
@ -19,5 +28,9 @@ const SCRIPT = `
|
|||
`;
|
||||
|
||||
export function ThemeScript() {
|
||||
return <script dangerouslySetInnerHTML={{ __html: SCRIPT }} />;
|
||||
return (
|
||||
<Script id="theme-init" strategy="beforeInteractive">
|
||||
{SCRIPT}
|
||||
</Script>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue