Use use server and use client directives to control rendering and data access.
// app/page.tsx — server component, no directive required
export default async function Home() {
const res = await fetch("https://api.example.com/data", { cache: "no-store" });
const data = await res.json();
return <div>{data.title}</div>;
}export default async function AdminPage() {
const users = await db.user.findMany();
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}// ❌ Not allowed in a Server Component — move to a Client Component
// const [count, setCount] = useState(0);
// window.addEventListener(...);import { cache } from "react";
export const getUser = cache(async (id: string) => db.user.findUnique({ where: { id } }));
// getUser(id) called in Header and Sidebar = only one DB query"use client";
// ↑ must appear before any import statements
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}"use client";
// Both files below become part of the client bundle:
import { Chart } from "./Chart"; // ← client-side
import { DataGrid } from "./DataGrid"; // ← client-side"use client";
// ❌ Pulls entire lodash into the client bundle
import _ from "lodash";
// ✅ Move heavy computation to a Server Component or Server Action// Good: only the interactive button is a Client Component
// app/post/page.tsx — Server Component (no directive)
import { LikeButton } from "./LikeButton"; // only this file has "use client"
export default async function PostPage() {
const post = await getPost();
return <article>{post.body}<LikeButton id={post.id} /></article>;
}// components/MapWrapper.tsx
"use client";
import { MapComponent } from "some-map-library"; // uses window internally
export default function MapWrapper({ center }) {
return <MapComponent center={center} />;
}"use server";
export async function submitForm(formData: FormData) {
const email = formData.get("email");
// Save to database securely
return { success: true };
}"use client";
async function handleSubmit(formData: FormData) {
const result = await submitForm(formData);
}// app/form.tsx (server component)
import { saveContact } from "./actions";
export default function ContactForm() {
return <form action={saveContact}><input name="email" /><button>Send</button></form>;
}"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
if (!title || title.length < 3) return { error: "Title too short" };
await db.post.create({ data: { title } });
return { ok: true };
}"use client";
// ClientShell.tsx — the children prop can be a Server Component
export default function ClientShell({ children }) {
return <div>{children}</div>;
}
// app/page.tsx (Server Component) — ServerSidebar stays on the server
<ClientShell><ServerSidebar /></ClientShell>"use client";
// ❌ Cannot import a server component directly from a client file
import { ServerSidebar } from "./ServerSidebar"; // runtime error// ❌ Plain server function cannot be passed as a prop
<ClientComp onClick={serverFunction} />
// ✅ Server Action CAN be passed as a prop
"use server";
export async function deleteItem(id: string) { await db.item.delete({ where: { id } }); }
// Then in a Server Component: <ClientComp action={deleteItem} />// ✅ OK: strings, numbers, arrays, plain objects, Promises
<ClientChart data={data} title="Sales" />
// ❌ Not OK: class instances, Maps, Sets, functions (unless Server Actions)
<ClientComp handler={someClassInstance} /># Open DevTools > Network > JS
# Server-only modules (db, secrets) should NOT appear in client chunks// Good: server component fetches data
export default async function Posts() {
const posts = await getPosts();
return posts.map(post => <Post key={post.id} post={post} />);
}"use client";
// Only interactive components
export function Favorite({ postId }) {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>{liked ? "Liked" : "Like"}</button>;
}import "server-only";
// This file throws an error if imported in a client bundle
export async function getSecretData() { ... }// Wrong: functions can't cross the server-client boundary as props
<ClientComp onClick={serverFunction} /> // ❌
// Use server actions instead
<ClientComp action={serverAction} /> // ✅# Open browser DevTools > Network > JS
# Check that server-only components don't appear in client bundlesAdd 'use client' when your component uses browser APIs, React hooks like useState or useEffect, or event listeners. Components that only fetch data or render static content should stay as server components.
Yes — Server Components can import and render Client Components, but not the other way around. Pass Server Components as children or props to Client Components if you need to compose them.
Placing 'use server' at the top of a file marks all exported functions as Server Actions, while placing it inside an individual async function marks only that function. Use the inline form when you need just one Server Action inside a Client Component.
If the component accessing the secret has 'use client' or is imported by a Client Component, it runs on the client. Move secret access into a Server Component or Server Action where the code never leaves the server.
No — Server Components can call fetch or query a database directly without any directive, since they already run only on the server by default in the App Router. 'use server' is specifically for Server Actions (functions called from the client).