diff --git a/docs/advanced-features/dynamic-import.md b/docs/advanced-features/dynamic-import.md index d2ff95843271c..9bd60726971b5 100644 --- a/docs/advanced-features/dynamic-import.md +++ b/docs/advanced-features/dynamic-import.md @@ -156,45 +156,3 @@ function Home() { export default Home ``` - -## With suspense - -In React 18, `` and `React.lazy` can work with SSR. So dynamic provide an option `suspense` to let you choose if you want to load a component under suspense. - -```jsx -import { Suspense } from 'react' -import dynamic from 'next/dynamic' - -const Profile = dynamic(() => import('./profile'), { suspense: true }) - -function Home() { - return ( -
- - - -
- ) -} - -export default Home -``` - -If you set option `suspense` to true, other options will be omitted, delegating rendering control to React ``. -It's similar to using `React.lazy` directly, but `next/dynamic` will support mode like CSR and SSG. - -To use `suspense`, you need to enable `reactRoot` and `concurrentFeatures` in `next.config.js`. - -Note: `reactRoot` will be enabled by default when using React 18. - -```jsx -// next.config.js -module.exports = { - experimental: { - reactRoot: true, - concurrentFeatures: true, - }, -} -``` - -If you only enable `reactRoot` but not `concurrentFeatures`, suspense will always render fallbacks on server side. diff --git a/errors/no-suspense-in-blocking-mode.md b/errors/no-suspense-in-blocking-mode.md deleted file mode 100644 index 373966b07923b..0000000000000 --- a/errors/no-suspense-in-blocking-mode.md +++ /dev/null @@ -1,25 +0,0 @@ -# No suspense in blocking rendering - -### Why This Error Occurred - -Using `next/dynamic` with `suspense` option set to true under blocking rendering mode will lead to this error. - -Concurrent rendering mode is required to be enabled here with using `suspense` option. - -### Possible Ways to Fix It - -Make sure using React v18 and set `reactRoot`, `concurrentFeatures` to `true` in next.config.js to enable concurrent rendering mode. - -```jsx -// next.config.js -module.exports = { - experimental: { - reactRoot: true, - concurrentFeatures: true, - }, -} -``` - -### Useful Links - -- [Dynamic Import Usage Reference](https://nextjs.org/docs/advanced-features/dynamic-import) diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 08fdde92a6a32..82b54a3181dd8 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -1185,7 +1185,7 @@ export default async function getBaseWebpackConfig( config.reactStrictMode ), 'process.env.__NEXT_REACT_ROOT': JSON.stringify(hasReactRoot), - 'process.env.__NEXT_CONCURRENT_MODE': JSON.stringify( + 'process.env.__NEXT_CONCURRENT_FEATURES': JSON.stringify( config.experimental.concurrentFeatures && hasReactRoot ), 'process.env.__NEXT_OPTIMIZE_FONTS': JSON.stringify( diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 8933e817afb7f..fdc515ed7321f 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -1007,8 +1007,7 @@ export async function renderToHTML( buffers.push(chunk) }) stream.once('end', () => { - const content = Buffer.concat(buffers).toString('utf-8') - resolve(content) + resolve(Buffer.concat(buffers).toString('utf-8')) }) const { diff --git a/packages/next/shared/lib/dynamic.tsx b/packages/next/shared/lib/dynamic.tsx index 5e9aa8ff8fe53..5bfcbc5f03349 100644 --- a/packages/next/shared/lib/dynamic.tsx +++ b/packages/next/shared/lib/dynamic.tsx @@ -114,11 +114,12 @@ export default function dynamic

( loadableOptions = { ...loadableOptions, ...options } const suspenseOptions = loadableOptions as LoadableSuspenseOptions

- if (!process.env.__NEXT_CONCURRENT_MODE) { + if (!process.env.__NEXT_CONCURRENT_FEATURES) { // Error if react root is not enabled and `suspense` option is set to true if (!process.env.__NEXT_REACT_ROOT && suspenseOptions.suspense) { + // TODO: add error doc when this feature is stable throw new Error( - `Disallowed suspense option usage with next/dynamic, read more: https://nextjs.org/docs/messages/page-without-valid-component.` + `Disallowed suspense option usage with next/dynamic in blocking mode` ) } suspenseOptions.suspense = false diff --git a/test/integration/react-18/app/components/bar.js b/test/integration/react-18/app/components/bar.js index 73113a0d03d62..44a8eb0864dc1 100644 --- a/test/integration/react-18/app/components/bar.js +++ b/test/integration/react-18/app/components/bar.js @@ -4,10 +4,6 @@ import { useCachedPromise } from './promise-cache' const Foo = dynamic(() => import('./foo'), { suspense: true, - loadableGenerated: { - modules: ['./foo'], - webpack: [require.resolveWeak('./foo')], - }, }) export default function Bar() { diff --git a/test/integration/react-18/test/basics.js b/test/integration/react-18/test/basics.js index b6b13c490107d..bb3c4ad99ced4 100644 --- a/test/integration/react-18/test/basics.js +++ b/test/integration/react-18/test/basics.js @@ -19,7 +19,7 @@ export default (context) => { expect(res2.status).toBe(200) }) - it('should not preload modules on server side', async () => { + it('should render fallback without preloads on server side', async () => { const html = await renderViaHTTP(context.appPort, '/suspense/no-preload') const $ = cheerio.load(html) const nextData = JSON.parse($('#__NEXT_DATA__').text())