typescriptadvanced
Server/Client Component Patterns
Correctly compose server and client components with data fetching, interactivity, and composition patterns.
typescriptPress ⌘/Ctrl + Shift + C to copy
// Pattern 1: Server Component fetches, Client Component renders interactive UI
// components/PostList.tsx (Server Component — no 'use client')
async function PostList() {
// This runs on the server — direct DB/API access
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 },
}).then(r => r.json());
return (
<div className="space-y-4">
{posts.map((post: { id: string; title: string; body: string }) => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
// components/PostCard.tsx ('use client' for interactivity)
'use client';
import { useState } from 'react';
function PostCard({ post }: { post: { id: string; title: string; body: string } }) {
const [liked, setLiked] = useState(false);
return (
<div className="bg-[#111] border border-white/10 rounded-xl p-4">
<h3 className="text-white font-bold">{post.title}</h3>
<p className="text-gray-400 mt-1">{post.body.slice(0, 100)}...</p>
<button
onClick={() => setLiked(!liked)}
className={`mt-2 text-sm ${liked ? 'text-red-400' : 'text-gray-500'}`}
>
{liked ? '\u2764 Liked' : '\u2661 Like'}
</button>
</div>
);
}
// Pattern 2: Composition — passing Server Components as children to Client Components
// ClientShell.tsx
'use client';
function ClientShell({ children, sidebar }: { children: React.ReactNode; sidebar: React.ReactNode }) {
const [sidebarOpen, setSidebarOpen] = useState(true);
return (
<div className="flex">
{sidebarOpen && <aside className="w-64">{sidebar}</aside>}
<main className="flex-1">{children}</main>
<button onClick={() => setSidebarOpen(!sidebarOpen)}>Toggle</button>
</div>
);
}
// Page.tsx (Server Component)
// export default async function Page() {
// const data = await getData(); // server-only
// return (
// <ClientShell sidebar={<ServerSidebar />}>
// <ServerContent data={data} />
// </ClientShell>
// );
// }
// Pattern 3: Server-only module guard
// lib/server-only.ts
// import 'server-only'; // Throws if imported in client component
// export async function getSecretData() { return process.env.SECRET; }
export { PostList, PostCard, ClientShell };Sponsored
Vercel
Use Cases
- RSC architecture planning
- Server/client boundary design
- Interactive data-driven pages
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptintermediate
Server Component Composition Patterns
Compose server and client components effectively with the donut pattern and children pass-through.
Best for: interactive layouts
#nextjs#rsc
typescriptintermediate
Route Groups for Layout Organization
Use route groups to share layouts across routes without affecting URL structure.
Best for: Marketing vs. dashboard layouts
#nextjs#routing
typescriptbeginner
Server to Client Component Data Passing
Pass data from server components to client components using props and serialization patterns.
Best for: data hydration
#nextjs#server-components
typescriptintermediate
Event-Driven Architecture Pattern
Build loosely coupled systems with typed event bus, async event handlers, and domain event patterns.
Best for: Microservice communication
#nodejs#events