Home >Web Front-end >JS Tutorial >Server vs. Client Components in Next.js When and How to Use Them
Next.js 13 introduced React Server Components, giving developers the power to choose where and how to render components—either on the server for performance or on the client for interactivity. This flexibility allows us to build apps that combine speed and dynamic capabilities.
In this article, we’ll explore not just the basics, but also dive into how to use server components within client components—a common need when building dynamic, efficient apps.
Server components are rendered entirely on the server and don’t require any client-side JavaScript. They’re perfect for static content like headers, footers, or even data-driven components that don't need user interaction.
// app/components/Header.js export default function Header() { return ( <header> <h1>My Static Header</h1> </header> ); }
This component is rendered on the server and doesn't involve any client-side interaction, meaning it loads faster with less JavaScript.
// app/components/PostList.js export default async function PostList() { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const posts = await res.json(); return ( <ul> {posts.slice(0, 5).map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
This PostList component fetches data on the server and sends the pre-rendered HTML to the client, ensuring faster load times.
Client components are essential when you need interactivity, such as form inputs, event listeners, or dynamic content. These components use JavaScript on the client to handle user interactions.
// app/components/SearchBar.js 'use client'; // This makes the component a client component import { useState } from 'react'; export default function SearchBar() { const [searchTerm, setSearchTerm] = useState(''); return ( <div> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="Search..." /> <p>Searching for: {searchTerm}</p> </div> ); }
The SearchBar is interactive, so it needs to be a client component. You can use the useState hook and other React hooks only in client components.
You might have a use-case to combine Server and Client Components, so let's talk on how to do that next:
A core strength of Next.js 13 is the ability to combine server and client components. A best practice is to use server components by default and push client components as deep as possible into your component tree.
// app/layout.js import SearchBar from './components/SearchBar'; export default function Layout({ children }) { return ( <div> <header>My Blog</header> <SearchBar /> {/* Client component for interactivity */} {children} </div> ); }
The SearchBar component handles client-side interactivity, while the rest of the layout is server-rendered, offering a balance between performance and interactivity.
On the other-way round, you might have a use-case to use server component inside a client component. Let's check out how to do that.
It’s important to understand that server components can be nested inside client components, but not imported directly into them. To include a server component in a client component, you pass it as children or a prop to avoid breaking the boundary between the two.
Here’s a real-world example where a server component is passed as a child to a client component:
// app/components/Header.js export default function Header() { return ( <header> <h1>My Static Header</h1> </header> ); }
In the above example:
This pattern allows you to use the benefits of server rendering (less JavaScript, improved performance) while still having client-side interactivity.
Many third-party libraries like authentication providers or UI components rely on React hooks, which can only be used in client components. Here’s how you can work around that limitation by wrapping third-party libraries inside client components:
// app/components/PostList.js export default async function PostList() { const res = await fetch('https://jsonplaceholder.typicode.com/posts'); const posts = await res.json(); return ( <ul> {posts.slice(0, 5).map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> ); }
By wrapping the third-party react-slick carousel in a client component, we can use it in the server-rendered page while still accessing client-side features like interactivity.
When passing data between server and client components, the props must be serializable (e.g., strings, numbers, booleans). Complex objects like functions or instances of classes can’t be passed.
// app/components/SearchBar.js 'use client'; // This makes the component a client component import { useState } from 'react'; export default function SearchBar() { const [searchTerm, setSearchTerm] = useState(''); return ( <div> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="Search..." /> <p>Searching for: {searchTerm}</p> </div> ); }
The UserCard client component can now dynamically render the data passed from the server component while ensuring that everything remains serializable and thus passes through the server-client boundary without issues.
With all said, it would be interesting to conclude this with best practises. Let's move to that next:
Here are a few tips for composing server and client components effectively:
Default to Server Components: Use server components wherever possible for static or data-driven content to reduce JavaScript load and improve performance.
Use Client Components for Interactivity: Only use client components where user interaction or browser-specific APIs are needed.
Move Client Components Down the Tree: Push client components as deep into the component tree as possible. This allows more of your app to be rendered on the server, boosting performance.
Pass Server Components as Children: If a server component needs to be used within a client component, pass it as children or a prop instead of directly importing it.
With Next.js 13, you have the flexibility to render components on both the server and the client. By defaulting to server components for static content and client components for interactivity, and by managing the boundary between the two carefully, you can build apps that are both fast and dynamic.
By following the patterns and examples here—like passing server components into client components and combining them thoughtfully—you’ll be able to leverage the full power of Next.js 13 to create highly performant, interactive web applications.
Happy coding
I am Michael.
The above is the detailed content of Server vs. Client Components in Next.js When and How to Use Them. For more information, please follow other related articles on the PHP Chinese website!