A few weeks ago, the React team gave the community a surprise gift - an introduction to React Server Components. React Server Components are pretty much what they sound like - React components that render on the server (rather than in the browser like traditional React components). Using React server components, developers can write components that render on the server in addition to rendering components in the browser. Components that render on the server can directly access data sources (such as databases and filesystems) without the need to query an API and can pass data to a client component as a prop (minimizing the need for the client to call an API).
There have been many great articles written about React Server Components, and this article will not be introducing any new concepts, but as part of my commitment to learning in 2021, I wanted to write a summary of what I've learned about this exciting new addition to React.
There were several motivating factors behind the creation of React Server Components (the RFC lists them all), but I wanted to focus on two of the problems that I see in my work that React Server Components solves: large bundle size and waterfalls in fetching data from the server.
Large bundle size.
When the browser is downloading a lot (which it does in large react applications or applications that import a lot of large third-party libraries), it can take a while to load the page, especially over slower connections. One of the struggles developers often face is how to improve the performance of their apps without sacrificing features. React server components helps solve this problem by sending less code to the browser.
Here's a concrete example of this:
Let's say you're working on an app that displays a note added by the user (which we do a version of in my team's product). You may want to display the date the note was created or last updated (which we also do). We use moment.js (which is currently in maintenance mode), which has a bundle size of 232 kB (66 kB gzipped). That's a lot of code for the browser to download. Since we're only displaying a date and not interacting with it at all, we could render that part of the note on the server and save the browser from having to download that large package.
However, this wouldn't work for all situations. My team's app also has some date inputs, where the user inputs a date (either by typing it in or using a date picker) and we need to validate that date - which we do using moment.js. Because these components are interactive, we would have to render them on the client and would have to load this large package in the browser. React server components allows us to minimize the instances where we have to load this particular library but does not completely eliminate it.
Waterfalls in fetching data from the server.
Waterfalls occur when data takes a long time to load, slowing down your applications. There can be many potential areas for waterfalls to occur, but one of the most common ones is on requests from the client to the server (and the server sending its response to the client). This back and forth communication between the server and client can cause significant latency and noticeably slow down your app, particularly if you have sequential requests coming from parent and child components. React server components solves this problem by fetching data on the server, eliminating that latency.
I actually would have benefited from React server components on a ticket I recently completed. I needed to retrieve data from one endpoint and based on that data, determine if I needed to fetch additional data by calling a second endpoint. I had to wait for the server to send me the response of the first API call, and then if the feature flag told me that I needed the additional info, I needed to make another API call. If I was rendering these components on the server, I would have saved myself a lot of time on the API calls.
Removing the waterfall between the client and server will significantly improve performance, but there can still be waterfalls on the server-side. The React team has indicated that there is a plan to provide an API to preload data requests as an optimization, which will help with the server-side waterfalls.
Under this new system, components can be rendered on the server, on the client (ie in the browser), or both. To differentiate between the different types of components, server components are named
*.server.js (or .jsx or .ts or whatever extension you're using), client components are named
*.client.js, and shared components are
Any component that requires interactivity or uses state must render on the client, and any component that directly accesses a database or filesystem must render on the server. Server components can import client components, but client components cannot import server components. Both server and client components can render shared components. The browser will only render client components and any shared components that they import, which greatly reduces the amount of data that is being rendered in the browser.
Here's a quick primer on what each type of component can and cannot do:
|Client Components||Server Components||Shared Components|
|Render in the Browser||✔️||❌||✔️|
|Render on the Server||❌||✔️||✔️|
|Can Use State||✔️||❌||❌|
|Can Use Rendering Lifecycle/effects||✔️||❌||❌|
|Can Use browser-ony APIs (such as the DOM)||✔️||❌||❌|
|Can User server-only data sources (such as databases, internal microservices, filesystems)||❌||✔️||❌|
|Can Render Server Components||❌||✔️||❌|
While I'd love to say that React server components will actually get your bundle size down to zero, it's important to remember that any component that requires state or interactivity will need to render in the browser, so if you have a highly interactive app, that will increase your bundle size. However, reorganizing your app in a way that makes the interactive sections smaller client components that can be rendered by server components will help reduce bundle size.
There will also be a steep learning curve, especially initially, and I recommend taking this slowly and practicing using server components in a non-production environment (especially right now, as server components are not production-ready) while you adjust to how they work.
I'm looking forward to trying out React server components (I plan to fork the React team's demo and play with it), but I don't see myself having a heavy use for it in my everyday life. I'm not currently working on any personal projects that require interactivity, and for the projects I'm working on, server-side rendering is probably a better way to reduce my bundle size.
Will I recommend it at work?
Probably not. I do think we could benefit from server components, but they wouldn't really work with our current architecture, and I don't think the benefit would be worth the cost involved. I would be interested in trying it if we were building a new product that was totally separate from our existing architecture. However, I don't see that happening at work any time soon, so for now, I think any chance I get to use React Server Components will be through playing around with the demo project.
- Introductory Talk from Dan Abramov and Lauren Tan - https://reactjs.org/blog/2020/12/21/data-fetching-with-react-server-components.html
- RFC from the React core team - https://github.com/reactjs/rfcs/pull/188
- React Server Components Demo - https://github.com/reactjs/server-components-demo