React Server Components
Prerequisites
- React 18 or later
- Next.js 13+ with the
app/
directory enabled - Familiarity with basic React concepts
Introduction to Server Components
Server Components allow you to render components on the server by default, sending fully rendered HTML to the client. This improves performance and reduces bundle size.
Introduced in React 18, Server Components enable selective server-side rendering without sacrificing interactivity. They fetch data on the server and ship only HTML, minimizing client JavaScript.
Key characteristics:
- Rendered entirely on the server
- No client-side JavaScript for pure UI
- Stateless and pure functions
Benefits
Server Components bring several advantages:
- Performance: Faster initial loads with pre-rendered HTML.
- Bundle Size: Smaller client bundles by excluding server-only logic.
- SEO: Enhanced search indexing with full HTML on load.
- Security: Keep sensitive operations on the server.
They integrate seamlessly with Client Components, so you can hydrate interactive parts as needed.
Usage and Syntax
By default, files under the app/
directory are Server Components. To convert a file into a Client Component, add "use client"
at the top.
// ServerComponent.tsx (default) export default function ServerComponent() { return <p>Server-rendered content</p>; }
// ClientComponent.tsx "use client" import { useState } from 'react' export default function ClientComponent() { const [count, setCount] = useState(0) return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button> }
You can nest Client Components inside Server Components:
export default function Page() { return ( <div> <ServerComponent /> <ClientComponent /> </div> ); }
Data Fetching Patterns
Server Components can fetch data directly using fetch
without increasing client bundle size. Next.js supports built-in caching and streaming:
export default async function UserList() { const res = await fetch('https://api.example.com/users', { cache: 'force-cache' }); if (!res.ok) throw new Error('Failed to fetch'); const users = await res.json(); return ( <ul> {users.map(user => <li key={user.id}>{user.name}</li>)} </ul> ); }
Use revalidate
or Next.js data fetching options for incremental updates.
Example Server Component
Here's a full example with data loading and rendering:
// app/docs/react/server-components/posts.tsx import React from 'react'; async function fetchPosts() { const res = await fetch('https://api.example.com/posts'); if (!res.ok) throw new Error('Error fetching posts'); return res.json(); } export default async function Posts() { const posts = await fetchPosts(); return ( <div> <h1>Latest Posts</h1> <ul> {posts.map(post => <li key={post.id}>{post.title}</li>)} </ul> </div> ); }
Best Practices
- Keep Server Components stateless and free of side-effects.
- Fetch only necessary data and handle errors gracefully.
- Leverage Suspense for loading states.
- Defer interactivity to Client Components.
Caveats and Limitations
Server Components cannot use browser-only APIs or React hooks like useState
or useEffect
. They must be pure functions.
Debugging requires server logs rather than browser devtools.