Next.js uses React APIs to render components on the server. Server Components are compiled into a special format called the React Server Component Payload (RSC), which includes:
On the client:
Hydration is the process of attaching event handlers to static HTML.
To define a Client Component, add 'use client' at the top of the file:
// app/ui/counter.tsx
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count} likes</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}Instead of marking entire layouts as Client Components, isolate interactive parts:
// app/layout.tsx
import Search from './search'
import Logo from './logo'
export default function Layout({ children }) {
return (
<nav>
<Logo />
<Search />
</nav>
<main>{children}</main>
)
}Use props to pass data from Server to Client Components:
// app/[id]/page.tsx
import LikeButton from '@/app/ui/like-button'
import { getPost } from '@/lib/data'
export default async function Page({ params }) {
const { id } = await params
const post = await getPost(id)
return <LikeButton likes={post.likes} />
}You can pass Server Components as children to Client Components:
// app/ui/modal.tsx
'use client'
export default function Modal({ children }) {
return <div>{children}</div>
}// app/page.tsx
import Modal from './ui/modal'
import Cart from './ui/cart'
export default function Page() {
return (
<Modal>
<Cart />
</Modal>
)
}React context is not supported in Server Components. Define it in a Client Component:
// app/theme-provider.tsx
'use client'
import { createContext } from 'react'
export const ThemeContext = createContext({})
export default function ThemeProvider({ children }) {
return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}Wrap third-party components that rely on client-only features in a Client Component:
// app/gallery.tsx
'use client'
import { Carousel } from 'acme-carousel'
export default function Gallery() {
return <Carousel />
}Only variables prefixed with NEXT_PUBLIC_ are exposed to the client. To protect server-only logic, use the server-only package:
// lib/data.ts
import 'server-only'
export async function getData() {
const res = await fetch('https://...', {
headers: { authorization: process.env.API_KEY }
})
return res.json()
}Next.js enables powerful composition of Server and Client Components. By using 'use client' strategically, managing context, and protecting environment variables, you can build scalable, secure, and interactive applications with optimal performance.