Using Server Components Inside Client Components

profile image

In Next.js, all components are server components by default. Let's explore how to use them inside client components.

This post has been translated by Jetbrains's Coding Agent Junie junie logoPlease let me 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 features (e.g., useState, onClick, etc.). What should you do in such cases?

To use a client component, add the 'use client' directive at the top of the file.

jsx
'use client'

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

This creates a client boundary from that component, and all components and module files imported in the component operate on the client side. (What's important here is not the component's rendering hierarchy but the import relationship.)

Therefore, components without 'use client' are also automatically converted to client components.

What about this case? Let's declare a server component inside a client component.

jsx
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;
jsx
'use client'

import ServerComponent from './ServerComponent'

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

You'll see an error log with the following warning: img01.png

When using client components and server components together, there are important rules:

  • ✅ Client component under a server component (Possible)
  • ✅ Client component under a client component (Possible)
  • ❌ Server component under a client component (Not possible)

🔧 Solution: Using the Props Pattern

So how can we use server components inside client components? The answer is simple: pass them as props.

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

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

Why Does It Work This Way?

The key is that the Client Boundary is determined by the import location, not the parent-child relationship of components. In the example above, ServerComponent is imported by App, which is a server component, so it remains a server component.

ClientComponent simply knows where to render the children it receives as props, but doesn't know what components will come. This pattern allows you to use server components even when wrapped in providers declared with use client in Layout.

Deep Dive

Server components operate in a completely different way from traditional server-side rendering (SSR). Unlike SSR, which generates complete HTML on the server, server components generate a special form of JSON-like stream called React Server Components (RSC) Payload. img02.png

This RSC Payload includes component tree structure, data, HTML content, etc., and the server serializes basic data types like strings, numbers, arrays, and built-in objects like React elements, Date objects, Maps, Sets, etc., to include in this payload. On the other hand, functions, class instances, closures, and event listeners cannot be serialized.

Therefore, you cannot pass values that cannot be serialized as props from server components to client components!

When rendering server components, if a client component is encountered, that part is marked with a special placeholder. This placeholder includes references to the client component and props information, allowing the client to render that component accurately.

The client converts the RSC payload received from the server into a form that React can understand and hydrates the content received from the server. At this point, the corresponding client component is rendered in the part marked as a placeholder.


References

❤️ 0
🔥 0
😎 0
⭐️ 0
🆒 0