Server and client components
In the App Router every component is a server component by default: it runs on the server, and only ready HTML goes to the browser. A component with state, effects or event handlers has to run in the browser — such a component is called a client component and is marked with the 'use client' directive.
A server component
Every page in the course so far has been a server component. That is why the post page could be async and call await getPost(slug):
// app/posts/[slug]/page.tsx
import { getPost } from '@/lib/posts';
export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
console.log('Reading post', slug);
return <h1>{post?.title}</h1>;
}Open /posts/hello-nextjs: the "Reading post" message appears in the terminal running npm run dev, not in the browser console. The code of this component never reaches the browser.
A client component
A Like button keeps a counter in state, so it needs the browser. Create components/LikeButton.tsx:
// components/LikeButton.tsx
'use client';
import { useState } from 'react';
export default function LikeButton() {
const [likes, setLikes] = useState(0);
return <button onClick={() => setLikes(likes + 1)}>Likes: {likes}</button>;
}The 'use client' directive goes on the very first line of the file, before the imports. It tells Next.js: send this component and everything it imports to the browser.
Combining them
A server component can render a client one:
// app/posts/[slug]/page.tsx (fragment)
import LikeButton from '@/components/LikeButton';
// ...inside the post markup
<LikeButton />The post page shows a button, and the counter grows on each click. The post itself is rendered on the server; only the button comes alive in the browser.
What works where
| Feature | Server | Client |
|---|---|---|
async and await in the component | Yes | No |
| Reading the database and secrets | Yes | No |
useState, useEffect | No | Yes |
onClick, onChange | No | Yes |
window, localStorage | No | Yes |
Passing data
A server component passes data to a client one through props: <LikeButton initial={10} />. The values must be convertible to JSON — strings, numbers, arrays, plain objects. You cannot pass a function; the exception is server actions from a later lesson.
Server first
A client component is also first rendered to HTML on the server, and then React "brings it to life" in the browser by attaching the handlers. This process is called hydration.
Common mistake
Adding 'use client' to every file "just in case". Then all the code, including data access, moves to the browser, the page loads slower and async components stop working. Mark only small interactive parts such as buttons and forms, and keep pages as server components.