vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.48k stars 26.8k forks source link

JS / CSS Code splitting in app dir does not work as described in the docs #61574

Open BleddP opened 8 months ago

BleddP commented 8 months ago

Link to the code that reproduces this issue

https://github.com/BleddP/app-dir-code-splitting-bug

To Reproduce

  1. Install the reproduction repo with yarn
  2. Run the app with yarn dev (or you can just go straight ahead and yarn build && yarn start

I have created four variations of the same page, they all use the spread operator as we want dynamic params to render a page from let's say a CMS.

Then, both pages (...bundled and ...components) import their components through a wrapper. One variant is wrapped in a server component, which attempts 1 regular import and 1 dynamic import. The other is wrapped in a client component, which attempts 1 regular import and 1 dynamic import.

The problem here is that the dynamic import only works when components are not exported from index.ts and when they are wrapped in a client wrapper. This is entirely unclear from the current Next.js docs.

Current vs. Expected behavior

According to the next.js docs: https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading

It allows you to defer loading of Client Components and imported libraries, and only include them in the client bundle when they're needed. For example, you might want to defer loading a modal until a user clicks to open it.

This implies that inside a server component, I can use dynamic to lazy load a client component. But as evidenced by my reproduction repo, there is absolutely no code splitting / lazy loading happening when dynamically importing a client component in a server component.

Expected vs current behaviour Expected: Any component that is imported using the dynamic import, is lazy loaded. Current: No component (not even client components) are lazy loaded if using dynamic from within a server component

Additionally, the CSS is bundled together into the main page css, if the component is also dynamically imported from within a server component. It only splits the CSS, if the component is exclusively dynamically imported from another client component. This is very confusing, especially because the lazy loading really does not work as described in your docs.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.2.0: Wed Nov 15 21:53:18 PST 2023; root:xnu-10002.61.3~2/RELEASE_ARM64_T6000
Binaries:
  Node: 18.17.0
  npm: 9.6.7
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 14.1.1-canary.27 // Latest available version is detected (14.1.1-canary.27).
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.1.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

App Router, Dynamic imports (next/dynamic)

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local)

Additional context

No response

wesnolte commented 7 months ago

Thanks for writing this up. I have the same issue. My first load JS on some routes is huge because of it.

bluebeel commented 5 months ago

I can confirm the next/dynamic doesn't work. The solution we did for now was using React.lazy where we could. But yeah quite the regression compared to Pages Router ^^'