diff --git a/errors/invalid-dynamic-suspense.md b/errors/invalid-dynamic-suspense.md index 11e4d6134d732fa..14a3d67e2cbd9ae 100644 --- a/errors/invalid-dynamic-suspense.md +++ b/errors/invalid-dynamic-suspense.md @@ -2,11 +2,22 @@ #### Why This Error Occurred -`` is not allowed under legacy render mode when using React older than v18. +- You are using a React version older than 18 with `{ suspense: true }`. +- You are using `{ suspense: true, ssr: false }`. #### Possible Ways to Fix It -Remove `suspense: true` option in `next/dynamic` usages. +**If you are using `{ suspense: true }` with React version older than 18** + +- You can try upgrading to React 18 or newer +- If upgrading React is not an option, remove `{ suspense: true }` from `next/dynamic` usages. + +**If you are using `{ suspense: true, ssr: false }`** + +Next.js will use `React.lazy` when `suspense` is set to true. React 18 or newer will always try to resolve the Suspense boundary on the server. This behavior can not be disabled, thus the `ssr: false` is ignored with `suspense: true`. + +- You should write code that works in both client-side and server-side. +- If rewriting the code is not an option, remove `{ suspense: true }` from `next/dynamic` usages. ### Useful Links diff --git a/packages/next/shared/lib/dynamic.tsx b/packages/next/shared/lib/dynamic.tsx index 91b747e4e1ea30e..f09bda7110168ba 100644 --- a/packages/next/shared/lib/dynamic.tsx +++ b/packages/next/shared/lib/dynamic.tsx @@ -107,11 +107,18 @@ export default function dynamic

( // Error if Fizz rendering is not enabled and `suspense` option is set to true if (!process.env.__NEXT_REACT_ROOT && loadableOptions.suspense) { - throw new Error( + console.warn( `Invalid suspense option usage in next/dynamic. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense` ) } + if (loadableOptions.ssr === false && loadableOptions.suspense) { + loadableOptions.ssr = true + console.warn( + `"ssr: false" is ignored by next/dynamic because you can not enable "suspense" while disabling "ssr" at the same time. Read more: https://nextjs.org/docs/messages/invalid-dynamic-suspense` + ) + } + // coming from build/babel/plugins/react-loadable-plugin.js if (loadableOptions.loadableGenerated) { loadableOptions = { diff --git a/test/e2e/dynamic-with-suspense/index.test.ts b/test/e2e/dynamic-with-suspense/index.test.ts index b0f24fb96a1e6e0..824d3785ea92862 100644 --- a/test/e2e/dynamic-with-suspense/index.test.ts +++ b/test/e2e/dynamic-with-suspense/index.test.ts @@ -49,7 +49,13 @@ suite('dynamic with suspense', () => { it('should render client-side', async () => { const browser = await webdriver(next.url, '/') + const warnings = (await browser.log()).map((log) => log.message).join('\n') + expect(await hasRedbox(browser)).toBe(false) + expect(warnings).toMatch( + /"ssr: false" is ignored by next\/dynamic because you can not enable "suspense" while disabling "ssr" at the same time/gim + ) + await browser.close() }) })