Is React full-stack now? A look at Server Components
React Server Components radically change how we build web applications. They change the mental model of React applications. But in doing so, they also bring clarity where confusion used to reign.
The NextJS team has just announced that the app/
directory, Next’s implementation of React Server Components, is stable enough to be used in production. This is the perfect opportunity to dive into React Server Components, to explore what they are. And why we need to learn how to use them. And how React Server Components change how we code in React and in NextJS.
And truth be told, I set up my personal website to make use of Next’s app/
directory, and I’ve been developing various features using the app router and Next JS’s implementation of React Server Components. And I’ve experienced first-hand the fact that you don’t just need to code differently; you also need to think differently about fundamentals such as state and how data flows through your application.
Let’s start by looking at
What are React Server Components, and why learn how to use them?
To state things as simply as possible: A React Server Component is a React Component that is meant to be run on the server. Being on the server opens new possibilities. However, it also introduces some new constraints. And above all, it requires a new mental model. Before exploring those, let’s start by looking at why: Why run React Components on the Server?
There are basically two reasons why React Server Components were introduced: trust and speed.
First, speed: Because the server is located right next to the database, it can fetch all the data it needs locally without having to go through the internet. This means pages that load faster and that, therefore, also provide a better user experience and rank better.
The server lets us keep secrets safe: passwords, API keys, scores, game logic, card numbers, and more. A common mistake I see junior developers make is that they tend to think they can trust their end users to use the code as they intended. The truth of the matter is that most users will use our code as intended. But some won’t. And this means we need to take that into account. We can’t trust our users to keep our secrets safe, so we need to store them on the server and on the server only.
But from a user’s perspective, React Server Components’ greatest strength is their potential to improve the performance of web applications. Server Components let developers write code that runs on the server side, rendering HTML output and sending it to the client.
This results in faster load times, improved performance, and a better user experience, since it significantly reduces the JavaScript payload.
React Server Components are also faster because they can access back-end resources directly. Client-side React needs to access database information via API calls, which means going through the internet.
Traditional client-side React components can’t directly access databases or other server-side resources without going through an API. Any complex business logic, or anything that requires several round trips with the server, benefits from being close to where the data resides, that is to say, on the database.
Because Server Components can access this backend data directly, they end up containing less code. Or, to frame things differently, the development process is more straightforward because they are fewer steps.
React Server Components also encourage good performance practices by default. They encourage you to split your app code based on its actual usage. To send only what's really necessary for whatever component is on screen.
It also makes it simpler for frameworks like Next to split the code, and only send the JavaScript that is actually needed for the interactivity. And that JavaScript doesn’t increase in size with your application and can be cached.
All of these benefits make React Server Components a powerful tool for making your web application faster and more secure. And they provide a better, more fun development experience.
However, React Server Components are not without their own pitfalls and difficulties.
React Server Components introduce new difficulties
First, as I mentioned previously, coding with React Server Components is a whole new mental model. In particular, how we think about state and data flow needs to change when using React Server Components.
React Server Components, as their name implies, run on the server, not the client. This means they can’t access browser APIs or the DOM (Document Object Model).
In a sense, this constraint brings greater clarity. In Next JS, in the code for a page, client-side and server-side are co-located in the same file, making for numerous Stack Overflow questions from people wondering why they couldn’t access the window or the document APIs. So maybe this is a good thing.
However, the DOM and Browser APIs aren’t the only things that don’t exist on the server. React Server Components also can’t store local state, which means useState and useContext are out.
Nor can Server Components handle events such as the onClick event. This also means you can’t use the useEffect hook in a Server Component.
And there is an important side-effect of these constraints: client-side React Component can’t directly include Server Components.
React Server Components require a new mental model
All these differences fundamentally change how we think about and design applications and components in React. When I was first developing using React Server Components, it took me some time to shift my mental model and understand how to structure my code.
I like to use the Component Tree as a starting point for my mental model — information flows down the component tree. And the server is a part of that data flow. In fact, it’s at the top. It is the primary source of that data. This is why client components can’t include server components. Client components can, of course, change data locally to manage the state of the interface. But they can’t mutate the server data on their own.
But you know what? React Server Components’ constraints force me to have a clearer mental model of what is happening in my code.
For example, you can’t just stick in a useContext
at the top of the component tree any more. useContext
is a client-side hook. You can’t use a useState
hook everywhere.
By delineating what is meant to be run on the client and what is meant to be run on the server, React is adding a constraint, a discrimination in the primary sense of the word, that is to say: a separation between things that are different.
And to some, this feels like a breaking change. And perhaps it is, at least in terms of the mental model to apply. I found myself thinking a lot more about where I should put which hook. And a lot of libraries I use, for authentification, translation, and more don’t work yet on the server. So (for example) I had to roll my own solution to make my website multilingual.
But here’s my take: like TypeScript, Server Components’ constraints help make things clearer, simpler, and more fun. React Server Components (and Next JS’s implementation) provide better mental clarity.
React is now a doorway to backend development. React Server Components truly change the name of the game.
Literally.
Developing in React is now a full-stack job. And that, my friends, is why we need to learn all about React Server components.
Stat.
And that’s why I recommend you watch this next video.
We help you better understand software development. Receive the latest blog posts, videos, insights and news from the frontlines of web development