Get the FREE Ultimate OpenClaw Setup Guide →

server-components

npx machina-cli add skill davepoon/buildwithclaude/server-components --openclaw
Files (1)
SKILL.md
5.9 KB

React Server Components in Next.js

Overview

React Server Components (RSC) allow components to render on the server, reducing client-side JavaScript and enabling direct data access. In Next.js App Router, all components are Server Components by default.

Server vs Client Components

Server Components (Default)

Server Components run only on the server:

// app/users/page.tsx (Server Component - default)
async function UsersPage() {
  const users = await db.user.findMany() // Direct DB access

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

Benefits:

  • Direct database/filesystem access
  • Keep sensitive data on server (API keys, tokens)
  • Reduce client bundle size
  • Automatic code splitting

Client Components

Add 'use client' directive for interactivity:

// components/counter.tsx
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}

Use Client Components for:

  • useState, useEffect, useReducer
  • Event handlers (onClick, onChange)
  • Browser APIs (window, document)
  • Custom hooks with state

The Mental Model

Think of the component tree as having a "client boundary":

Server Component (page.tsx)
├── Server Component (header.tsx)
├── Client Component ('use client') ← boundary
│   ├── Client Component (child)
│   └── Client Component (child)
└── Server Component (footer.tsx)

Key rules:

  1. Server Components can import Client Components
  2. Client Components cannot import Server Components
  3. You can pass Server Components as children to Client Components

Composition Patterns

Pattern 1: Server Data → Client Interactivity

Fetch data in Server Component, pass to Client:

// app/products/page.tsx (Server)
import { ProductList } from './product-list'

export default async function ProductsPage() {
  const products = await getProducts()
  return <ProductList products={products} />
}

// app/products/product-list.tsx (Client)
'use client'

export function ProductList({ products }: { products: Product[] }) {
  const [filter, setFilter] = useState('')

  const filtered = products.filter(p =>
    p.name.includes(filter)
  )

  return (
    <>
      <input onChange={e => setFilter(e.target.value)} />
      {filtered.map(p => <ProductCard key={p.id} product={p} />)}
    </>
  )
}

Pattern 2: Children as Server Components

Pass Server Components through children prop:

// components/client-wrapper.tsx
'use client'

export function ClientWrapper({ children }: { children: React.ReactNode }) {
  const [isOpen, setIsOpen] = useState(false)

  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
      {isOpen && children} {/* Server Component content */}
    </div>
  )
}

// app/page.tsx (Server)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'

export default function Page() {
  return (
    <ClientWrapper>
      <ServerContent /> {/* Renders on server! */}
    </ClientWrapper>
  )
}

Pattern 3: Slots for Complex Layouts

Use multiple children slots:

// components/dashboard-shell.tsx
'use client'

interface Props {
  sidebar: React.ReactNode
  main: React.ReactNode
}

export function DashboardShell({ sidebar, main }: Props) {
  const [collapsed, setCollapsed] = useState(false)

  return (
    <div className="flex">
      {!collapsed && <aside>{sidebar}</aside>}
      <main>{main}</main>
    </div>
  )
}

Data Fetching

Async Server Components

Server Components can be async:

// app/posts/page.tsx
export default async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts')
    .then(res => res.json())

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

Parallel Data Fetching

Fetch multiple resources in parallel:

export default async function DashboardPage() {
  const [user, posts, analytics] = await Promise.all([
    getUser(),
    getPosts(),
    getAnalytics(),
  ])

  return (
    <Dashboard user={user} posts={posts} analytics={analytics} />
  )
}

Streaming with Suspense

Stream slow components:

import { Suspense } from 'react'

export default function Page() {
  return (
    <div>
      <Header /> {/* Renders immediately */}
      <Suspense fallback={<PostsSkeleton />}>
        <SlowPosts /> {/* Streams when ready */}
      </Suspense>
    </div>
  )
}

Decision Guide

Use Server Component when:

  • Fetching data
  • Accessing backend resources
  • Keeping sensitive info on server
  • Reducing client JavaScript
  • Component has no interactivity

Use Client Component when:

  • Using state (useState, useReducer)
  • Using effects (useEffect)
  • Using event listeners
  • Using browser APIs
  • Using custom hooks with state

Common Mistakes

  1. Don't add 'use client' unnecessarily - it increases bundle size
  2. Don't try to import Server Components into Client Components
  3. Do serialize data at boundaries (no functions, classes, or dates)
  4. Do use the children pattern for composition

Resources

For detailed patterns, see:

  • references/server-vs-client.md - Complete comparison guide
  • references/composition-patterns.md - Advanced composition
  • examples/data-fetching-patterns.md - Data fetching examples

Source

git clone https://github.com/davepoon/buildwithclaude/blob/main/plugins/nextjs-expert/skills/server-components/SKILL.mdView on GitHub

Overview

React Server Components (RSC) render on the server, reducing client-side JavaScript and enabling direct data access. In Next.js App Router, components are Server Components by default, with Client Components activated via the 'use client' directive. This skill helps you architect with server vs client boundaries, optimize performance, and compose components across the boundary.

How This Skill Works

Server Components run on the server and can directly access databases and files, delivering HTML to the client and shrinking the client bundle. Client Components enable interactivity and browser APIs, and must opt into the client boundary using 'use client'. Remember the rules: Server Components can import Client Components, but Client Components cannot import Server Components; you pass Server Components as children to Client Components to enable mixed rendering.

When to Use It

  • When you need direct server-side data access (e.g., database queries) without exposing secrets to the client.
  • When you want to minimize client-side JavaScript by rendering heavy UI on the server.
  • When you require interactive UI only in specific parts of the page, guarded by Client Components.
  • When you want to compose a page using server-rendered content and still support client interactivity via props.
  • When managing sensitive data or API keys that should stay on the server.

Quick Start

  1. Step 1: Identify code that can run on the server and mark it as a Server Component.
  2. Step 2: Move data fetching to the server component and pass results to a client component.
  3. Step 3: Add 'use client' only to components that require interactivity, and verify import directions.

Best Practices

  • Fetch data in Server Components whenever possible and pass it down to Client Components as props.
  • Keep Client Components small and focused on interactivity (state, effects, event handlers).
  • Only use 'use client' in components that truly need client-side state or browser APIs.
  • Respect the import rules: Server Components can import Client Components; avoid importing Server Components into Client Components.
  • Profile and test the boundary to ensure no unnecessary data exposure or extra client JS.

Example Use Cases

  • Product catalog: server fetches products; a Client Component handles filtering and search.
  • Blog feed: server renders list; client component adds like/bookmark interactions.
  • Dashboard widgets: server-rendered data panels with a client-side dashboard for UI state.
  • Header with navigation: server renders static parts; a client wrapper handles menu toggles.
  • Modal forms: server-delivered content within a Client Component that manages visibility.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers