Client Components Rendering on Client Side or Server Side #54114
Replies: 4 comments 15 replies
-
Client components are rendered during any form of SSR, not just during pre-rendering.
This document is pretty good at eliminating any doubts: Maybe the "primarily", word is not the best choice there, but I do believe it is there to show "contrast" to React Server's components, all-in behavior.
So the wording indicates how Client components are, mainly designed to achieve a set of features that give interactivity to a page, but are also designed to be part of the SSR output tree. The primary goal you'd have when using a Client Component is to gain interactivity. |
Beta Was this translation helpful? Give feedback.
-
Thanks for clear explanation. There are still few questions requiring clarification:
|
Beta Was this translation helpful? Give feedback.
-
hey , In Nextjs Docs (Page: Lazy Loading > Skipping SSR) "Client Components will be pre-rendered (SSR) by default." i am also confused here? is this current?? if not plz clarify it. when Nextjs Docs say "Client Components will be pre-rendered (SSR) by default" does that mean React Client component JavaScript get's render on the Server???? and server send pre-render React Client component JavaScript file?? i use to think that only the React Server Component gets render on the server and server send pre-render RSC Payload??? i am confused?? |
Beta Was this translation helpful? Give feedback.
-
So I've been studying the rendering strategies in Next.js for a while now. I must say they are confusing. The docs are good, but not good enough IMHO. The following is a part of the notes I took about the topic. It probably has some factual errors (not sure), but I thought it would be good to share it here anyway. Maybe someone who knows better can comment on it: CODE EXECUTION
+ Server: server components / route handlers / middleware / server actions
+ Client: client components
FETCH API CACHING/MEMOIZATION
+ fetch() is memoized across a single render pass for
+ Server components e.g. Layout / Page
+ generateStaticParams()
+ generateMetadata()
+ fetch() can access Next.js data cache in
+ Server components (res = memo ? memo : cached ? cached : await request())
+ Server actions
+ Route handlers
+ (fetch() works in middleware, but I'm not sure it can access data cache)
TOOLS FOR CONFIGURING CACHING / REVALIDATION
+ Caching
+ routeConfig.fetchCache
+ routeConfig.revalidate
+ unstable_noStore()
+ FetchOptions.cache (individual calls to fetch())
+ Revalidation
+ Route segment config: revalidate
+ FetchOptions.next.revalidate
+ FetchOptions.next.tag
+ revalidateTag()
+ revalidatePath()
DATA FETCHING / CACHING / REVALIDATION
+ Server components:
+ Fetching: fetch() / ORM / DB client / others
+ Caching: FetchOptions.cache / React.cache / unstable_cache
+ Revalidation:
+ FetchOptions.next.tags
+ FetchOptions.next.revalidate
+ revalidateTag
+ revalidatePath
+ Client components:
+ Fetching: browser fetch() + Route handlers + Query library
+ Caching: Query library cache
+ Revalidation: Query library revalidation
+ Middleware:
+ Fetching: fetch() / others
+ Caching: -- (not sure if FetchOptions.cache works here)
+ Revalidation: -- (not possible AFAIK)
+ Route handlers:
+ Fetching: fetch() / ORM / DB client / others
+ Caching: -- FetchOptions.cache / unstable_cache
+ Revalidation:
+ FetchOptions.next.tags
+ FetchOptions.next.revalidate
+ revalidateTag
+ revalidatePath
+ Server actions:
+ Fetching: fetch() / ORM / DB client / others
+ Caching: -- FetchOptions.cache / unstable_cache
+ Revalidation:
+ FetchOptions.next.tags
+ FetchOptions.next.revalidate
+ revalidateTag
+ revalidatePath
FACTORS DETERMINING STATIC/DYNAMIC ROUTE
+ Determinants (affect static/dynamic rendering for routes)
+ Route segment config:
+ dynamic
+ fetchCache
+ revalidate
+ dynamic accessors (refs/functions)
+ cookies()
+ headers()
+ unstable_after()
+ unstable_noStore()
+ PageProps.searchParams
+ FetchOptions.cache (individual calls to fetch())
+ FetchOptions.next.revalidate
+ Non-determinants (do not affect static/dynamic rendering for routes)
+ unstable_cache
+ React.cache
+ useSearchParams
+ Route segment config: dynamicParams
+ generateMetadata()
+ generateStaticParams()
STATIC/DYNAMIC ROUTES DETERMINATION
+ Dynamic: If not static
+ Static: If one of the ff is true
+ routeConfig.dynamic="force-static"
+ routeConfig.dynamic="error"
+ routeConfig.dynamic="auto" + no dynamic accessors +
every FetchOptions.cache="force-cache" +
every FetchOptions.next.revalidate!=0 + routeConfig.revalidate!=0
NOTES ON STATIC/DYNAMIC ROUTES DETERMINATION
+ Values that affect `FetchOptions.cache` and `FetchOptions.next.revalidate`
+ routeConfig.dynamic
+ routeConfig.fetchCache
+ routeConfig.revalidate
+ useSearchParams() affects type of client component, not type of route
+ generateMetadata() can use Next.js fetch but doesn't affect rendering type
+ routeConfig.dynamicParams / generateStaticParams() affect paths list but not
path/route types
+ Revalidatable route is static
+ Dynamically rendered routes can have cached data, see link below
STATIC VS DYNAMIC ROUTES
+ Static: Rendered at build time or at revalidation time.
+ Dynamic: Rendered at each request time.
STATIC/DYNAMIC COMPONENTS DETERMINATION
+ Server components:
+ Static: when route is static
+ Dynamic: when route is dynamic. See notes below.
+ Client components:
+ Static: when component is not dynamic
+ Dynamic:
+ when route is dynamic
+ useSearchParams(): when the component (a client component) or a child
(client) component uses useSearchParams(), and the child in not wrapped in
<Suspense />. This is mentioned in the docs for useSearchParams(). Also,
see notes below.
NOTES ON STATIC/DYNAMIC COMPONENTS DETERMINATION
+ Generally speaking, as of the time of writing this, suspense can be triggered
by throwing a Promise from within a component. See
https://stackoverflow.com/questions/59791769/what-is-the-react-official-position-for-throwing-promises-inside-render-function.
I believe this is what the Next.js useSearchParams() uses under the hood. I
also asked @tkDodo, one of the contributors to Tanstack Query about this on
Discord, and he said it is the same thing used to implement the `useSuspenseQuery` hook,
i.e. a Promise is thrown from the hook. See the exact message here:
https://discord.com/channels/719702312431386674/1278285342193090572/1278369228449386527.
You can also see this feature in discussion here:
+ https://github.com/facebook/react/issues/17621
+ https://github.com/facebook/react/issues/17526
+ However, this feature looks like it is intentionally undocumented in React
documentation. This suggests that is isn't meant for public use and is only
used by library authors that work closely with the react team (I guess). That
said, it is worth noting that at the time of writing this, throwing a promise
from any component (server/client) will trigger Suspense and cause the
component to suspense until the promise is resolved or rejected. (Note that
rejection also triggers rerender as mentioned in the second GitHub issue
above.) Therefore, such component (or some other component above it) is
expected to be wrapped in <Suspense /> so that:
+ The other parts of the UI that aren't suspended can be streamed.
+ A fallback can be rendered for the suspended UI.
+ That said, it seems that the new `use()` API from react will be the official
way to trigger suspense. Docs: https://react.dev/reference/react/use
STATIC VS DYNAMIC COMPONENTS
+ Server components
+ Static: Can't / doesn't use dynamic accessors
+ Dynamic:
+ Can use dynamic functions
+ Page from page.tsx can use searchParams` props
+ Generally used with <Suspense />
+ Client components
+ Static: Can't use useSearchParams()
+ Dynamic:
+ Can use useSearchParams()
+ Can be used with <Suspense />
STATIC VS DYNAMIC COMPONENTS: BUILD TIME VS RUNTIME
+ Server components
+ Static:
+ Build time: component -> rsc payload -- (prerendering)
+ Runtime: rsc payload downloaded/rendered -- (no hydration)
+ Dynamic:
+ Build time: --
+ Runtime: component -> rsc payload -> downloaded/rendered -- (no hydration)
+ Client components (Still runs on server - so requires useEffect() for browser
APIs - except when lazy loading with SSR set to false; See next/dynamic)
+ Static:
+ Build time: component -> static HTML -- (prerendering)
+ Runtime:
+ Full load: sHTML downloaded+rendered -> client update code downloaded
-> client HTML hydrated
+ Navigation: component code downloaded -> component rendered+hydrated
+ Dynamic:
+ Build time: --
+ Runtime:
+ Full load: component -> sHTML -> downloaded+rendered
-> client update code downloaded -> client HTML hydrated
+ Navigation: component code downloaded -> component rendered+hydrated
NOTES ON STATIC/DYNAMIC COMPONENTS DETERMINATION
+ If the route is determined to be dynamic, the whole route (all components) are
rendered dynamically, not just some. We are expected to use cached data to
optimize performance by caching results across requests using the cache APIs
eg. FetchOptions.cache, FetchOptions.next.revalidate, unstable_cache, etc.
RENDERING STRATEGIES SIMULATION
+ SSR: Dynamic route
+ CSR: Static route + client API calls
+ SSG: Static route
+ ISR: Static route + path/tag revalidation
STREAMING UI
+ Client components:
+ Static: If wrapped with Suspense, there'll be no difference unless there's a
parent or child component that suspends, in which case the whole wrapped
tree will be suspended.
+ Dynamic: If the route is static, the fallback is returned in the initial
HTML coming from the server, and then replaced with the actual client
component when React hydrates the component on the browser and Next.js gives
it the appropriate search params. If the route is dynamic, the search params
are directly available for the client component on the server. (Note that
useSearchParams() causes the client component to be dynamically rendered up
to the nearest Suspense boundary.)
+ Server components:
+ Static: If wrapped with Suspense, there'll be no difference unless there's a
parent or child component that suspends, in which case the whole wrapped
tree will be suspended.
+ Dynamic: For server components, the fallback is returned while the async
server component is awaited, and then used to replace the fallback when
ready.
``` |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I am confused by how docs describe client components. It states client components are pre-rendered on the server and hydrated on the client, but it also says components in the Client Component module graph are primarily rendered on the client.
My questions is that in which context client components will be pre-rendered on the server and in which context it will be rendered on client. Please kindly clarify.
The official docs say:
Client Components
Client Components enable you to add client-side interactivity to your application. In Next.js, they are pre-rendered on the server and hydrated on the client. You can think of Client Components as how components in the Pages Router have always worked.
Good to know:
Components in the Server Component module graph are guaranteed to be only rendered on the server.
Components in the Client Component module graph are primarily rendered on the client, but with Next.js, they can also be pre-rendered on the server and hydrated on the client.
The "use client" directive must be defined at the top of a file before any imports.
"use client" does not need to be defined in every file. The Client module boundary only needs to be defined once, at the "entry point", for all modules imported into it to be considered a Client Component.
Link: https://nextjs.org/docs/getting-started/react-essentials#client-components
Beta Was this translation helpful? Give feedback.
All reactions