Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[breaking] 13.0.7 next/dynamic: fallback no longer rendered for client side async component using suspense: true and <Suspense fallback={...}> #45116

Closed
1 task done
cvbuelow opened this issue Jan 20, 2023 · 2 comments · Fixed by #45565
Labels
Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). linear: next Confirmed issue that is tracked by the Next.js team.

Comments

@cvbuelow
Copy link
Contributor

cvbuelow commented Jan 20, 2023

Verify canary release

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

Provide environment information

next: 13.0.7
react: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

Dynamic imports (next/dynamic)

Link to the code that reproduces this issue

https://codesandbox.io/p/sandbox/dynamic-with-v13-0-7-1bkhgf

To Reproduce

You may need to throttle your network speed to see the fallback loading/not loading and click reload on the sandbox browser

13.0.6 works: https://codesandbox.io/p/sandbox/dynamic-with-v13-0-6-h2iq5i
13.0.7 broken: https://codesandbox.io/p/sandbox/dynamic-with-v13-0-7-1bkhgf
13.1.4-canary.0 still broken: https://codesandbox.io/p/sandbox/dynamic-with-canary-dd9bqz

Describe the Bug

#42589 introduced a breaking change for client side only async components using suspense: true and <Suspense fallback={...}>. The fallback is no longer rendered since now next/dynamic renders its own <Suspense> block with fallback set from the loading option.

For this example the fallback is no longer rendered:

const MyAsync = dynamic(() => import("./MyAsync"), { suspense: true });
const Loading = () => 'Loading...'

const Component = () => (
  <Suspense fallback={<Loading />}>
    <MyAsync />
  </Suspense>
)

To get it to work again I'm required to change my code to use loading option with next/dynamic, or use React.lazy directly if my Loading component needs to receive props from the render flow.

Visual example

13.0.6 with fallback rendered

Screen.Recording.2023-01-20.at.1.32.25.PM.mov

13.0.7 fallback is not rendered

Screen.Recording.2023-01-20.at.1.31.03.PM.mov

Expected Behavior

I wouldn't expect there to be breaking changes when upgrading a patch version. Breaking changes should be called out in the release notes. I can work around this issue by making code changes, but ideally next/dynamic should detect if suspense: true option is set and revert to the old behavior in that case to maintain backwards compatibility.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

@cvbuelow cvbuelow added the bug Issue was opened via the bug report template. label Jan 20, 2023
@huozhi huozhi added kind: bug and removed bug Issue was opened via the bug report template. labels Jan 20, 2023
@github-actions github-actions bot added the linear: next Confirmed issue that is tracked by the Next.js team. label Jan 20, 2023
@Ivowainer
Copy link

Ivowainer commented Jan 21, 2023

This issue is introduced in Next.js version 13.0.7, specifically in the functionality of asynchronous components that use the "suspense: true" attribute and the component. The fallback functionality is no longer being rendered because next/dynamic is now using its own block with the fallback set from the load option. This change broke backwards compatibility and is not mentioned in the release notes. The problem can be solved by changing the code to use the loading option with next/dynamic, or by using React.lazy directly if the Loading component needs to receive properties from the render stream.

Here is an example of how to use the load option with next/dynamic to solve this problem:

import dynamic from 'next/dynamic'

const MyAsync = dynamic(() => import("./MyAsync"), {
  loading: () => <p>Loading...</p>,
});

const Component = () => (
  <div>
    <MyAsync />
  </div>
);

export default Component

In this example, the dynamic component "MyAsync" is imported using next/dynamic, and a loading component is set using the "loading" option. This way, while the "MyAsync" component is loading, it will show the load component instead.

It is important to mention that if you want to continue using the fallback functionality of React.Suspense, you will need to use the React.lazy function.

@huozhi huozhi added Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). and removed kind: bug labels Feb 3, 2023
@kodiakhq kodiakhq bot closed this as completed in #45565 Feb 4, 2023
kodiakhq bot pushed a commit that referenced this issue Feb 4, 2023
## Issue

To address the problem that we introduced in 13.0.7 (#42589) where we thought we could use same implementation `next/dynamic` for both `pages/` and `app/` directory. But it turns out it leads to many problems, such as:

* SSR preloading could miss the content, especially with nested dynamic calls
  * Closes #45213
* Introducing suspense boundary into `next/dynamic` with extra wrapped `<Suspense>` outside will lead to content is not resolevd during SSR
  * Related #45151
  * Closes #45099
* Unexpected hydration errors for suspense boundaries. Though react removed this error but the 18.3 is not out yet.
  * Closes #44083
  * Closes #45246
 
## Solution

Separate the dynamic implementation for `app/` dir and `pages/`. 

For `app/` dir we can encourage users to: 
  * Directly use `React.lazy` + `Suspense` for SSR'd content, and `next/dynamic` 
  * For non SSR components since it requires some internal integeration with next.js.

For `pages/` dir we still keep the original implementation

If you want to use `<Suspense>` with dynamic `fallback` value, use `React.lazy` + `Suspense` directly instead of picking up `next/dynamic` 
  * Closes #45116

This will solve various issue before react 18.3 is out and let users still progressively upgrade to new versions of next.js.

## Bug Fix

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have a helpful link attached, see [`contributing.md`](https://github.com/vercel/next.js/blob/canary/contributing.md)
@github-actions
Copy link
Contributor

github-actions bot commented Mar 6, 2023

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Lazy Loading Related to Next.js Lazy Loading (e.g., `next/dynamic` or `React.lazy`). linear: next Confirmed issue that is tracked by the Next.js team.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants