typescriptintermediate

Server Component Composition Patterns

Compose server and client components effectively with the donut pattern and children pass-through.

typescript
// Pattern 1: The "Donut" Pattern
// Server component wraps client component with server-fetched data

// app/profile/page.tsx (Server Component)
import { ProfileEditor } from './ProfileEditor';

export default async function ProfilePage() {
  const user = await getUser(); // Server-side fetch

  return (
    <div className="max-w-2xl mx-auto">
      <h1>Profile</h1>
      {/* Pass server data as props to client component */}
      <ProfileEditor
        initialData={user}
        serverRenderedAvatar={
          <img src={user.avatar} alt={user.name} className="w-20 h-20 rounded-full" />
        }
      />
    </div>
  );
}

// Pattern 2: Children pass-through
// Client component accepts server component as children

// components/Tabs.tsx
'use client';
import { useState } from 'react';

export function Tabs({
  tabs,
}: {
  tabs: { label: string; content: React.ReactNode }[];
}) {
  const [active, setActive] = useState(0);

  return (
    <div>
      <div className="flex gap-2 border-b">
        {tabs.map((tab, i) => (
          <button
            key={tab.label}
            onClick={() => setActive(i)}
            className={active === i ? 'border-b-2 border-blue-500' : ''}
          >
            {tab.label}
          </button>
        ))}
      </div>
      <div className="p-4">{tabs[active].content}</div>
    </div>
  );
}

// app/page.tsx (Server Component)
import { Tabs } from '@/components/Tabs';

export default async function Page() {
  const posts = await getPosts();
  const comments = await getComments();

  return (
    <Tabs
      tabs={[
        {
          label: 'Posts',
          content: <PostList posts={posts} />, // Server Component!
        },
        {
          label: 'Comments',
          content: <CommentList comments={comments} />, // Server Component!
        },
      ]}
    />
  );
}

Use Cases

  • interactive layouts
  • tabbed content
  • modal wrappers

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.