How do Server and Client Components work?

In my previous blog, we discussed about the different rendering strategies. Next, we will be discussing about how server and client components work in Next.js. Let’s dive into it.

How do Server and Client Components Work?

On the server

On the server, Next.js uses the React API to start the rendering process. The rendering process is split into chunks, by individual route segments (layouts, pages, etc.)

  • Server components are rendered on the server in a special format called React Server Components payload or RSC payload.

  • Client components and RSC payload are used to prerender the HTML.

    • What does prerendering the HTML mean?

      • This means when the static HTML shell is created, parts of the route which are dynamic in nature, are replaced with a fallback user interface in that static HTML shell, which is later asynchronously replaced by client components, when the user visits the route. This entire process is called prerendering

      • The parts of a route which are dynamic in nature makes use of the RSC payload to reconcile the client and the server component tree, and hydrate the client component to add interactivity to those components.

What is RSC payload?

The RSC payload is a complex binary representation of React Server Component Tree and is optimised for streaming.

The RSC payload contains:

  • The server rendered output of server components.

  • Placeholders for client components where they should be rendered and references to their JavaScript instructions using which they will be hydrated and rendered.

    • These placeholders are minimal HTML shells that are eventually replaced by actual content when the client component gets rendered and hydrated.
  • Any props that will be passed from server components to client components.

💡Streaming is the process of putting parts of the component that depends on request time data under a Suspense boundary, thereby, showing a loading UI while the content is being rendered in the server, and eventually replacing the loading UI.

Streaming allows one to segregate different parts of the user interface in a section into separate components, and render the content in parallel, resulting in reduced initial load times, better performance, and overall reduction in round trips from clients to servers and vice-versa.

import Loading from "../ui/Loading";

export default function Page() {

    return (
        <main>
            These are my playlists.
            <Suspense fallback={<Loading />}>
                <OldSongs />
            </Suspense>
            <Suspense fallback={<Loading />}>
                <NewSongs />
            </Suspense>
            <Suspense fallback={<Loading />}>
                <MovieSoundtracks />
            </Suspense>
        </main>
    );
}

// Each of those components in the suspended boundaries will be streamed 
// in parallel ensuring the content appears gradually as the request from the 
// server is processed.

On the client

On the client, when the user visits a route, the following process happens in sequence:

  • The static HTML is used to immediately show a fast non-interactive preview of the route to the user.

  • The RSC payload is used to reconcile the server and the client.

  • JavaScript instructions sent along with the payload is used to hydrate the client components resulting in interactivity.

On subsequent navigations to the same route, the RSC payload is prefetched and cached for quick navigations, and the client components are completely rendered on the browser instead of showing the static HTML.

It is only during the initial load, the static HTML is shown for better SEO, and performance.

Now, that’s out of the way, I would like to discuss something about Partial Prerendering which is Next.js’s experimental feature.

Enter Partial Prerendering

There are two ways in which Next.js renders routes - Static, and Dynamic.

Partial Prerendering is a combination of the both and is used for routes that host static information combined with dynamic interactive sections.

Parts of the route which are dynamic are shown to have some kind of Loading UI until the content is rendered, and hydrated on the client, and eventually replacing the Loading UI. For the rest of the page, which are static in nature, the client shows the server rendered static HTML.

Partial prerendering is a sweet spot which combines the power of Static rendering for quick display of content and Dynamic rendering for those parts of the route which are highly dynamic and requires fresh content to be served to user upon request.

So how do I enable partial prerendering?

Before you enable partial prerendering, it’s good take note of the fact that it is an experimental feature, and should not used for production grade applications.

That said, here’s the code snippet below that gives insight into usage of partial prerendering in a project.

import User from "./User";
import SkeletonLoader from "./SkeletonLoader";

import { Suspense } from "react";

export const experimental_ppr = true;

export default function Page() {
    return (
        <section>
            <h1>This will be partially prerendered</h1>
            <Suspense fallback={<SkeletonLoader />}>
                <User />
            </Suspense>
        </section>
    );
}

// It's important to put the dynamic parts of a route in Suspense boundary.
// Otherwise it will code will throw an error.

That’s it for this blog. If you like it, please give it a thumbs up. If you got any doubts, feel free to post it in the comments, and I will try reply as soon as possible. Thank you for your time, and patience.

0
Subscribe to my newsletter

Read articles from Nikhil Kumar Patra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nikhil Kumar Patra
Nikhil Kumar Patra

Web Development fanatic, excited about learning anything related to the same, from the basics to the advanced. I am a final year undergraduate at the International Institute of Information Technology, Bhubaneswar. I like to solve problems and learn about complex algorithms, and new technologies, and I aspire to create an impact in the world of Computer Science someday ❤️. Apart from Web Development, I am currently exploring and learning about iOS App development.