Exploring Next.js 13 Server Components | by Yiming Cao | Nov, 2022

Quick overview of Next.js 13 beta features

Next.js 13 has landed in a somewhat confusing way. Several notable items have been added; However, a good portion is still beta. Still, beta features give us important hints about what the future of Next.js will be like, so there’s good reason to keep a close eye on them, even if you’re going to wait to adopt them.

This article is part of a series of experiences about beta features. Let’s play with server components today.

Making server components the default option is arguably the boldest change made in Next.js 13. Server components aim to reduce the size of the JS sent to the client by keeping the component code only on the server side. That is, rendering happens and only on the server side, even if the loading of the component is triggered on the client side (via client-side routing). This is a huge paradigm shift.

I first learned about React Server Components over a year ago from this video (watch later, it’s too long):

Until then it seems pretty “research-y”, so I was shocked to see that NeXT.JS is already betting its future on it. Time flies and the brilliant engineers at React must have done some really great work, so I created a shiny new NeXT.JS 13 project to play around with.

npx create-next-app@latest --experimental-app --ts --eslint next13-server-components

Let’s have some fun playing with the project. You can find the complete project code Here,

The first difference noticed is that a new app The folder now sits with our old friend page, I’ll save the routing changes for another article, but for now what’s worth noting is that each component app Folder is, by default, a server component, meaning it is rendered on the server side, and its code resides on the server side.

Let’s now create our first server component:

// app/server/page.tsx

export default function Server()
console.log('Server page rendering: this should only be printed on the server');
return (


Server Page


My secret key: process.env.MY_SECRET_ENV



);

if you access /server route, whether by a fresh browser load or client-side routing, you will only see the row of logs printed in your server console, but never in the browser console. Environment variable value is also obtained from server side.

Looking at the network traffic in the browser, you’ll see that the content of the server component is loaded via a remote call that returns an octet stream of JSON data of the render result:

server component network traffic

...
"childProp":
"current": [
[
"$",
"div",
null,

"children": [
["$", "h1", null, "children": "Server Page" ],
[
"$",
"p",
null,

"children": ["My secret key: ", "abc123"]

]
]

]
]

Rendering a server component is literally an API call to get the virtual DOM and then render it in the browser.

The most important thing to remember is that server components are meant to render non-interactive content, so there are no event handlers, no React hooks, and no browser-only APIs.

The most important advantage is that you can freely access any backend resources and secrets in the server components. It is more secure (data does not leak) and faster (code does not leak).

To create a client component, you’ll need to explicitly mark it as use client,

// app/client/page.tsx

'use client';

import useEffect from 'react';

export default function Client()
console.log(
'Client page rendering: this should only be printed on the server during ssr, and client when routing'
);

useEffect(() =>
console.log('Client component rendered');
);

return (


Client Page


/* Uncommenting this will result in an error complaining about inconsistent
rendering between client and server, which is very true */
/*

My secret env: process.env.MY_SECRET_ENV

*/

);

As you can already guess, this gives you the same behavior as previous Next.js versions.

This is rendered by SSR when the page first loads, so you should see the first log in the server console; During client-side routing, both log messages will appear in the browser console.

The biggest difference between Server Component and SSR is that SSR is at the page level, while Server Component, as its name suggests, is at the component level. This means that you can mix and match server and client components in the render tree as you wish.

// A server page containing client component and nested server component

// app/mixmatch/page.tsx
import Client from './client';
import NestedServer from './nested-server';

export default function MixMatchPage()
console.log('MixMatchPage rendering');
return (


Server Page








);

Mixing server and client components in one page

In this type of mixed scenario, the server and client components are rendered independently, and the results are assembled by the React runtime. Props passed from server components to clients are serialized (and need to be serialized) across the network.

One caution you need to make is that if a server component is imported directly into the client, it is silently obfuscated into the client component.

Let’s modify the previous example a bit to see this:

// app/degenerate/page.tsx

import Client from './client';

export default function MixMatchPage()
console.log('MixMatchPage rendering');
return (


MixMatch Server Page






);

// app/degenerate/client.tsx

'use client';

import NestedServer from './nested-server';

export default function Client( message : message: string )
console.log('Client component rendering');

return (


Client Child


Message from parent: message






);

If you check the log, you will see NestedServer Has “degenerated” and is now rendered by browsers.

Next.JS is doing everything possible to move things to the server side, just like people did web development two decades ago. So now we’re coming a full circle, but with a much better development experience and end user experience.

For end users, this is a clear win as computing on the server side is faster and more reliable. The result will be a much more intense first material paint.

For developers, the paradigm shift will be mentally challenging, mixed with confusion, bugs, and anti-patterns. It will be a journey to hell.

Thank you for reading.

Want to Connect?

I'm the creator of ZenStack, a toolkit for building
secure CRUD services with Next.js + TypeScript.
Our goal is to let you save time writing boilerplate code
and focus on building what matters - the user experience.

Leave a Reply