Using in server components, client components

🌐

This post has been translated by DeepL . Please let us know if there are any mistranslations!

Understanding server/client components

In Next.js App Router, all components are server components by default. However, sometimes you need client-side functionality (e.g. useState, onClick, etc.). How do you do this?

To use client components, you can add the 'use client' directive to the top of your file.

'use client'

export const ClientComponent = () => {
 return (
   <div> <h1>Client Component</h1
     <h1>Client Component</h1>
   </div> </div
 );
};

This creates a Client Boundary from that component, so all component and module files that are imported from the component will behave on the client side. (Note that it is the import relationship that matters, not the rendering hierarchy of the component.)

For this reason, even components that are not 'use client'are automatically converted to client components.

What about cases like this? Let's declare a server component inside a client component

import React from 'react';

export const ServerComponent = async () => {
  const data = await getData();
  return <div>{JSON.stringify(data)}</div>;
};

async function getData() {
  const res = await fetch('<https://jsonplaceholder.typicode.com/todos/1>');
  const json = await res.json();

  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  } return json;
}

export default ServerComponent;
'use client'

import ServerComponent from './ServerComponent'

export const ClientComponent = () => {
  return (
    <div> <h1>Client Component</h1
      <h1>Client Component</h1>
      <ServerComponent /> <div
    </div> </div>
  )
}

And you should see an error log with the following warning img01.png

There are some important rules when using client components and server components together, such as

  • Client components subclassing server components (possible)
  • Client components as children of server components (yes)
  • ❌ Server component under a client component (not possible)

πŸ”§ Workaround: Utilize the Props pattern

So how do you use a server component inside a client component? The answer is simple: pass it as props.

// The pattern in action
'use client'
export const ClientComponent = ({ children }) => {
  return (
    <div> <h1>Client Component
      <h1>Client Component</h1>
      {children}
    </div>
  );
};

const App = () => {
  return (
    <ClientComponent
      <ServerComponent />.
    </ClientComponent>
  )
}

Why does this work?

The key is that the Client Boundary is determined based on the import location, not the parent-child relationship of the components. In the example above, ServerComponentremains a server component because it was imported from App, which is a server component.

ClientComponentsimply knows where the children it receives as props will be rendered, but not which component they come from. This pattern allows you to use server components even if they are wrapped in a Provider declared as use client in a Layout.

Deep Dive

Server Components work in a completely different way than traditional server-side rendering (SSR). Unlike SSR, which generates finished HTML on the server, Server Components generate a special type of JSON-like stream called the React Server Components (RSC) Payload. img02.png

This RSC payload contains the component tree structure, data, and HTML content, and the server serializes basic data types like strings, numbers, arrays, and built-in objects like React elements, Date objects, or Map and Set objects to include them in this payload. On the other hand, things like functions, class instances, closures, and event listeners cannot be serialized.

Therefore, you can't pass values as propsfrom the server component to the client component that can't be serialized!

When a server component encounters a client component during the rendering process, that part is marked with a special placeholder. This placeholder contains the client component's reference and props information so that the client can render that component correctly.

The client converts the RSC payload it receives from the server into a form that React can understand, and hydrates the content it receives from the server. The client then renders the corresponding client component where the placeholder is marked.

See