Mixing without dragging
Rules of composition:
- A server component can render a client component. This is the normal direction: the page (server) sprinkles in interactive leaves (client).
- A client component cannot import a server component. Importing pulls it into the client bundle, where server code cannot run.
- But a client component can receive server-rendered content as
children. The classic props pattern from the React course is the escape hatch.
Pattern 3 is worth a picture in code:
// app/page.js (server) import Collapsible from "./collapsible"; // a "use client" wrapper import HeavyArticle from "./article"; // stays a server component export default function Page() { return ( <Collapsible> <HeavyArticle /> </Collapsible> ); }
Collapsible handles the open/close click in the browser, while HeavyArticle is rendered on the server and passed in as already-finished content.
Quiz
Why does the Collapsible pattern above keep HeavyArticle out of the browser bundle?
Quiz
A "use client" component adds import HeavyArticle from "./article" at the top of its file (article.js has no directive). What happens to HeavyArticle?
Choosing in practice
A quick decision list you can apply to any component:
- Needs
useState,useEffect, refs, event handlers, or browser APIs (localStorage,window)? → client. - Fetches data, reads env vars or a database, or just renders markup from props? → server (the default, do nothing).
- Both? Split it: a server component for the data and markup, with a small client child for the interactive part.
This mirrors the frontend/backend split you already lived in the React and Backend with Node.js courses, except the boundary now runs through one component tree instead of between two repos.
Problem
A blog page tree has these components: PostBody (renders markdown from props), CommentForm (textarea with useState), AuthorCard (renders props), ThemeToggle (onClick + localStorage). How many of them need "use client"?