Note: this post assumes some familiarity with React (the notion of components, in particular), and web development in general (usage of client-side JavaScript, communication between client and server, awareness of the DOM).
There's been a lot of buzz lately in the React community about server components. They came on the scene in 2022, have been production-ready since May 2023 (with the Next.js app
directory stable release). But what determines when to use a server component vs. a client component?
To answer this, it helps to understand what React server components are, and what problems they're trying to solve. Let's back up a bit and start with...
The key players
When a user interacts with a web site, there are four key players in the communication chain:
Breaking the chain: not allowed
In this communication chain, each player may only talk to its immediate neighbors. Choose a pair below for details on how they communicate (or don't communicate).
Select a pair of players
Where the React code runs
Because of the unbreakable communication chain, it matters where the React code runs. If the code runs on the client, there's no way to query the database directly (instead, it needs to run an API call via JavaScript to get the data from the server). And if the React code runs on the server, the client
React and Interactivity
React components are, well, reactive. That is, they can morph and change in response to user input or changing conditions, by way of re-rendering. Furthermore, the re-renders occur without needing to load another page from the server. This is the magic of any JavaScript that runs on the client (not just React).
But, as established above, the server can't run interactive JavaScript in response to user input. So any JavaScript that updates the DOM needs to run on the client.
It's worth taking a moment to consider what features require JavaScript to run on the client, and what types don't.
Dynamic Content Quiz
Dynamic Content Quiz
5 questions
Take a look at these examples, and determine whether they need to run JavaScript on the client.
Why not both?
Ok, so the components that need interactive JavaScript to run on the client can't be server components (since server components can't run JavaScript on the client). And client components can't make database calls, only API calls.
At this point, you may be thinking to yourself, I'd like to be able to access the database directly in my React components, and I want to use interactive JavaScript. Can't I do both?
Yes, you can! You'll need server-side rendering (aka
Here's the SSR sequence of events:
- The server queries the database and receives data (in this example, a list of dragon habitats).
- The server renders the React component, with the dragon habitats passed as props. At this point we have a static server component that's incapable of any interactive JavaScript on the client.
- The client receives both the rendered server component and the React code.
- The React code runs on the client, and the resulting render is used to hydrate the component. Hydration adds interactive JavaScript to the static rendered server component.
SSR in code
Here's what SSR might look like using the Next.js pages router:
SSR: The best of both worlds
SSR has the advantages of both a server render and a client render:
- The client doesn't need an API call to get the dragon habitats; that data comes along with the static server component.
- Hydration allows interactive JavaScript and dynamic content on the client.
SSR: The worst of both worlds
SSR also has all the disadvantages of both a server render and a client render. Namely the server sends the JavaScript to render the entire component (including all of its children), and then runs all of that JavaScript on the client. Imagine only a small part of the component is interactive — this is a lot of unnecessary bundle size and work on the client.
Enter: React server components
React server components (RSCs) allow us to choose: each component can be a server component or a client component (as opposed to SSR, where each component is a server component and a client component).
This allows us to be very surgical about where to use each kind of component, and apply the advantages of each where needed.
React server component benefits
To summarize the benefits of RSCs:
Benefit 1: simpler code
This is similar to an advantage of SSR, but the code is even simpler.
Since the component runs only on the server, the component code can contact the database (or read from the server filesystem) directly (whaaaat?). The client never runs the code, so there's no danger in breaking the communications chain.
Look at how much simpler the server component code is than the client component code when it comes to accessing the database.
A note on syntax: Next.js assumes all components are server components,
unless the component starts with the "use client";
directive at the top
of the file. That directive designates the component as a client component to
Next.js.
Client component:
Server component:
It takes the client component six lines and two hooks to get the data, whereas the server component only needs one line. Notice that server components can be asynchronous as well; they can wait for async functions to return before rendering on the server.
Benefit 2: increased website efficiency
Server components only render on the server, which is generally more powerful than the client. This has two advantages:
- Any computationally-intensive rendering will be faster on the server than it would be on the client.
- There's no need to send a large JavaScript packages to the client on how to render complex, static components — which saves on bundle size.
This Vercel blog post sums up the advantages quite well:
[T]he server, far more powerful and physically closer to your data sources, deals with compute-intensive rendering and ships to the client just the interactive pieces of code.
Understanding React Server Components (Vercel blog post)
React server component examples
The blog you're reading now uses a couple of React server components that illustrate the benefits.
Bright syntax highlighter
I'm using Bright for the code snippets on this blog. The package has tons of features, and a package size to match (38 kB). However, none of that JavaScript makes it to the client. All of the code snippets are rendered on the server, and the server sends only the final rendered HTML.
MDX Renderer
This blog is written in
There are some interactive parts of this blog which can't be server components (like the "communications chain" component). I'll get to the "server parent → client child" pattern in the next section.
When this blog post was written, the next-mdx-remote docs were a bit out-of-date. If you follow the link for React server components docs (above), you may see it describes the Next app router as unstable — but that hasn't been the case since May 2023.
When to use React server components
React server components shine when a component needs server resources. This might include direct access to the database or filesystem, or server computing power to render a complicated component. React server components also save on bundle size and client processing, since the components come to the client pre-rendered. Server components won't work if the UI involves interactivity or the component has state that changes and causes a
Why not both? (revisited)
But what if your page needs some of each? You need access to the filesystem, and you want to use server power to render static parts of the page — but you also need some interactivity. Guess what: the page you're reading right now fits that description.
There's a common pattern where the parent component is a React server component (in this case, my BlogPost
component, which reads the MDX from the filesystem and renders it into HTML). The parent RSC then contains child client components for interactive functionality. Here, BlogPost
is a parent to the interactive client components CommunicationsChain
and BlogQuiz
.
This pattern could also be used on an e-commerce site. Say there's a product listing page, with the products laid out in a grid, like this:
The product information comes from the database — but there are interactive components too. Mousing over each product reveals a "Quick look" button, which brings up a modal with more product information. Sometimes mousing over the image changes the product view as well. Plus there's a heart button for adding the product to your favorites, which goes from outline to filled when clicked.
The code for the product listing component might look something like this:
React Server Component Quiz
All right, time to see how confident you feel about when to use React server components!
React Server Component Quiz
7 questions
Which would you use in each of these situations? Server component? Client component? Or both (the server parent → client child pattern)?
Further reading
Some suggestions for where to go from here:
- This post omitted a major advantage of React server components: streaming and Suspense. See the Next.js docs for how server components can use streaming to deliver data as it becomes available — even after initial page load. Next.js also uses Suspense to indicate that data's coming.
- Understanding React Server Components is an excellent introductory blog post from Vercel.
- Josh W. Comeau's blog post Making Sense of React Server Components features useful graphics to visualize the sequence of events for Server components vs client components.
- Demystifying React Server Components with NextJS 13 App Router digs deeper into the technical details, with helpful examples.