Next.js Discord

Discord Forum

Clarification on Next.js initial HTML

Unanswered
Yellow croaker posted this in #help-forum
Open in Discord
Yellow croakerOP
Just a clarification..


maybe im misunderstood but for Next.js, when there is an initial request/hard refresh, the server sends
initial HTML (so client can see something quickly)
the entire JS code that includes all component in every route + global state management if any, to be ‘hydrated’.

So only then… page is interactable + any subsequent navigation will be done by client side without sending any more request to the server. 

am i actually wrong? Did Next.js always deliver client JS relevant only relevant to each route ? if im wrong, are JS-parts that are global or wrapped around the whole app like redux/react query is sent once instead?

Also, both Server Side Component and SSR does fetching/async operations on a server to generate HTML. What is the competitve edge for server side component ? When the blog says RSC interleaves between server/client component, what does that mean ?
does that mean, it gives the possibility to have server component (to-be-static html) nested in client component (js) nested in server component (to-be-static html) ?

6 Replies

Asian black bear
@Yellow croaker Both client and server components generate HTML on the server side and send it to the client. The main difference is indeed whether the server HTML is "hydrated".

The main advantage of client components are that you can do react stuff to them, but the main downside is that the JS bundle sent to hydrate them is quite large and scales linearly with document size.

The big exciting thing about server components is that they are only HTML and come with no JS bundle at all. They do not do react stuff at all, but for a lot of the document this does not matter. They solve an enormous problem with time to interactive and other core web metrics by avoiding a tremendous amount of unnecessary JS runtime.

Next has always sent to the client only what is necessary for the specific path being loaded. For client components and /pages pages, this is some HTML and a code-split JS bundle. Indeed the bundling process also includes some global stuff common to all routes, but that is not everything.
Asian black bear
The interleaving of server and client components actually works quite well. The main rule is that you cannot import a server component in a file marked "use client", because it will force it to be a client component. Client components can wrap server components though.

This works
ServerComponent.jsx
<ClientComponent>
  <ServerComponent>
    <Client2/>
  </ServerComponent>
</ClientComponent>


No
ClientComponent.jsx
'use client'
import ServerComponent from '../...'

export function Client(){
  return <ServerComponent/>
}
@Asian black bear <@245738645305163776> Both client and server components generate HTML on the server side and send it to the client. The main difference is indeed whether the server HTML is "hydrated". The main advantage of client components are that you can do react stuff to them, but the main downside is that the JS bundle sent to hydrate them is quite large and scales linearly with document size. The big exciting thing about server components is that they are only HTML and come with no JS bundle at all. They do not do react stuff at all, but for a lot of the document this does not matter. They solve an enormous problem with `time to interactive` and other core web metrics by avoiding a tremendous amount of unnecessary JS runtime. Next has always sent to the client only what is necessary for the specific path being loaded. For client components and /pages pages, this is some HTML and a code-split JS bundle. Indeed the bundling process also includes some global stuff common to all routes, but that is not everything.
Yellow croakerOP
thank you so much taking your time for the reply. Makes sense.
hmm, mind if I repeat this...

+ So, previously, without server components, SSR was a great feature, because it allowed inputs on request to dynamically build those initial-HTML served for clients (better first contentful paint).
- However, SSR is still using the same old JS client components. This requires sending a JS bundle to hydrate them as you said. I'm guessing this JS bundle is just react but more specifically like https://react.dev/reference/react-dom/client/hydrateRoot ?

+ Server component strictly delivers HTML that will not need to be hydrated. This saves unnecessary workload/network, or its better to handle that work on server side, or simply not necessary to move into client. Also while its just plain HTML, in Next.js it allows us to place react component to nested inside of it, which will load into client side.
? Would you say, each of those client component inserted, would act like calling createRoot(someNodeInsideHTML).render(<ClientComponent />); individually?

? When RootLayout is wrapped by a ReduxProvider. Does that affect at all to the nested server component, does it convert it to client component? Is Next.js smart enough to figure this out?
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
@Yellow croaker thank you so much taking your time for the reply. Makes sense. hmm, mind if I repeat this... + So, previously, without server components, SSR was a great feature, because it allowed inputs on request to dynamically build those initial-HTML served for clients (better first contentful paint). - However, SSR is still using the same old JS client components. This requires sending a JS bundle to hydrate them as you said. I'm guessing this JS bundle is just react but more specifically like https://react.dev/reference/react-dom/client/hydrateRoot ? + Server component strictly delivers HTML that will not need to be hydrated. This saves unnecessary workload/network, or its better to handle that work on server side, or simply not necessary to move into client. Also while its just plain HTML, in Next.js it allows us to place react component to nested inside of it, which will load into client side. ? Would you say, each of those client component inserted, would act like calling `createRoot(someNodeInsideHTML).render(<ClientComponent />);` individually? ? When RootLayout is wrapped by a ReduxProvider. Does that affect at all to the nested server component, does it convert it to client component? Is Next.js smart enough to figure this out? js export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body> <Providers>{children}</Providers> </body> </html> ); }
Asian black bear
On SSR of client components/classic React, besides the faster initial render it is also pretty important for SEO for the content to exist without needing JS to run. You are right about hydrateRoot, it is essentially the same concept. The bundles are the entire React app and are able to render the DOM without the HTML too.

The irony of server components is that they are kind of how React used to be done, as embeds of reactive parts within static HTML pages. It works kind of differently from distinct React roots, but the idea of React being only for active parts within a document of static HTML is aesthetically correct and a good way to think of it.

You are allowed to import client components into server components, and stuff passed through them as {children} or in props will not be changed. This is actually a really essential paradigm because you can not really tunnel props through server components, and context lets you make "bridges" across those inert barriers.

Redux is kind of a turd for the app router. Having it hydrate without a single source of truth data fetching method is REALLY hard. I have switched to Jotai and find it works REALLY well. https://jotai.org/
If you try it out, I have a paradigm for scoped provider contexts that is 🔥 so let me know 🙂