React Server Components (RSC) represent the most significant architectural shift in React's history since Hooks. Introduced with React 18 and stabilized in React 19, RSC allows components to run exclusively on the server — sending only HTML and a serialized component tree to the client with zero JavaScript bundle for those components. For teams building Next.js applications, understanding RSC is no longer optional.
Server Components vs Client Components: The Mental Model
Server Components run on the server during render and have zero client-side JavaScript. They can directly access databases, file systems, and environment variables. Client Components (marked with "use client") run in the browser and support interactivity, state, and browser APIs. Most components in a typical app only display data — making them Server Components by default dramatically reduces your JavaScript bundle.
The Boundary Between Server and Client
- "use client" creates a boundary — everything imported inside a client component becomes client code
- You can pass Server Components as children (props.children) to Client Components — a powerful pattern
- Server Components cannot use useState, useEffect, or browser APIs
- Client Components can import other Client Components, but not Server Components
- Server Components can be async — await your database calls directly in the component body
Practical Patterns That Work Well
The most effective pattern is to push interactivity as deep as possible in the component tree, keeping the outer shell as Server Components. For example, a product listing page where the layout, title, and grid are Server Components, but the "Add to Cart" button and filter dropdowns are isolated Client Components. This maximizes server rendering benefits while enabling necessary interactivity.
Common Mistake to Avoid
Adding "use client" to a layout or page component because one small child needs state. Instead, extract only the interactive element into a separate client component and keep everything else server-rendered.
Data Fetching and Caching in RSC
Server Components enable co-located data fetching without useEffect or external state management. You can fetch data directly in the component that renders it, making data flow explicit and eliminating waterfall loading states. Next.js extends the native fetch API with caching capabilities — deduplicate requests across a render, set cache TTLs, or opt into on-demand revalidation.
- Any component using useState, useReducer, or useContext must be a Client Component
- Components needing useEffect for side effects (timers, subscriptions) require "use client"
- Event handlers: onClick, onChange, onSubmit all require client components
- Browser-only APIs: localStorage, geolocation, IntersectionObserver need the client
- Third-party libraries relying on window or document objects need "use client"
“React Server Components are not just a performance optimization — they're a new model for thinking about the boundary between server and client in your application.”
Tags
