React Server Components: A New Era of Web Development
React Server Components: A New Era of Web Development
React Server Components (RSC) represent a fundamental shift in how we build React applications. Let's explore this paradigm.
What are Server Components?
Server Components are React components that render on the server and send their UI to the client without JavaScript.
Benefits
- Zero bundle size - No JavaScript sent to client
- Direct backend access - Query databases directly
- Automatic code splitting - Only interactive parts loaded
- Better performance - Less JavaScript to download
Server vs Client Components
// Server Component (default)
// This file has NO 'use client' directive
async function ProductList() {
// Direct database access
const products = await db.products.findMany();
return (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);
}
// Client Component
'use client';
import { useState } from 'react';
function AddToCartButton({ productId }: { productId: string }) {
const [isAdding, setIsAdding] = useState(false);
const handleClick = async () => {
setIsAdding(true);
await addToCart(productId);
setIsAdding(false);
};
return (
<button onClick={handleClick} disabled={isAdding}>
{isAdding ? 'Adding...' : 'Add to Cart'}
</button>
);
}
Composition Pattern
// Server Component
async function ProductPage({ id }: { id: string }) {
const product = await getProduct(id);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
{/* Pass server data to client component */}
<AddToCartButton
productId={product.id}
initialPrice={product.price}
/>
</div>
);
}
Data Fetching
// Server Component with async
async function UserProfile({ userId }: { userId: string }) {
// This runs on the server
const user = await fetchUser(userId);
const posts = await fetchUserPosts(userId);
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<h2>Posts</h2>
<PostList posts={posts} />
</div>
);
}
Streaming with Suspense
import { Suspense } from 'react';
function Page() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<LoadingSkeleton />}>
<SlowComponent />
</Suspense>
<Suspense fallback={<LoadingSkeleton />}>
<AnotherSlowComponent />
</Suspense>
</div>
);
}
// This streams to the client as each section loads
async function SlowComponent() {
const data = await slowFetch();
return <div>{data}</div>;
}
Error Boundaries
import { ErrorBoundary } from 'react-error-boundary';
function Page() {
return (
<ErrorBoundary fallback={<ErrorFallback />}>
<Suspense fallback={<Loading />}>
<ServerComponent />
</Suspense>
</ErrorBoundary>
);
}
Best Practices
- Default to Server Components - Use client components only when needed
- Push interactivity down - Keep parent components on server
- Use Suspense boundaries - For streaming and loading states
- Serialize props carefully - Props must be serializable
When to Use Client Components
- Event listeners (onClick, onChange, etc.)
- State and lifecycle hooks
- Browser-only APIs
- Custom hooks that use state
Conclusion
React Server Components offer a powerful new model for building React applications. By defaulting to server rendering and selectively adding interactivity, we get better performance while maintaining the React development experience we love.