vercel / next.js

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

Server components not dynamically sending the set of client components #55989

Open zxti opened 11 months ago

zxti commented 11 months ago

Link to the code that reproduces this issue

https://github.com/zxti/next-bundletest

To Reproduce

  1. Start the application
  2. Go to http://localhost:3000
  3. Open DevTools and inspect bundles

Current vs. Expected behavior

Current: sends both Cli1 and Cli2.

Expected: sending either Cli1 or Cli2 but not both.

The server component just renders this:

Math.random() > 0.5 ? <Cli1/> : <Cli2/>

From https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md

Server Components can dynamically choose which Client Components to render, allowing clients to download just the minimal amount of code necessary to render a page.

Have also seen various other mentions of this in interviews etc. (can't remember where exactly), but I cannot produce the advertised behavior.

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.6.0: Wed Jul  5 22:22:05 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T6000
Binaries:
  Node: 18.16.0
  npm: 9.5.1
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 13.5.4-canary.0
  eslint-config-next: 13.5.3
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/AOperating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 22.6.0: Wed Jul  5 22:22:05 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T6000
Binaries:
  Node: 18.16.0
  npm: 9.5.1
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 13.5.4-canary.0
  eslint-config-next: 13.5.3
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.2.2
Next.js Config:
  output: N/A

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

App Router

Additional context

No response

lbittner-pdftron commented 11 months ago

+1 I have noticed this issue as well. In a situation where client components are conditionally rendered based on some server side logic, all client components are bundled and sent to the client

georgecartridge commented 1 month ago

Another +1. I've been having some problems with this. Here's just a short example:

page.tsx

import { A } from "./A";

export default async function Page() {
    return(
        <>
            <A data={[{ type: 'B' }, { type: 'B' }]} />
        </>
    );
}

A.tsx

import { B } from "./B";
import { C } from "./C";

type Props = {
    data: {
        type: string
    }[]
}

export function A({ data }: Props) {
    return data.map(item => (
        <>
            {
                item.type === 'B'
                    ? <B />
                    : <C />
            }
        </>
    ))
}

B.tsx

'use client';

export function B() {
    return (
        <div>$$$B</div>
    );
}

C.tsx

'use client';

export function C() {
    return (
        <div>$$$C</div>
    )
};

Despite page.tsx and A.tsx being server components, and only being passed data to render <B />, it still bundles the <C /> component and delivers it to the client.