React 18 introduced a completely new paradigm to the frontend realm in the form of React Server Components (RSC). Next.js 13 followed suit by introducing support for RSC through their app directory, which is in beta now. This was in addition to the Server-Side Rendering (SSR) feature that Next.js has always been popular for. Now, it is natural to struggle to understand the differences between SSR and RSC at best, or at worst, you may even think these two are the same. This article is an attempt to clear the air.
Before we look at what RSC is and how it relates to SSR, it helps to understand how Single-Page Applications (SPA) work. To that end, let’s use the example of a simple application, which I have very creatively named Personal Assistant. This app has two pages, namely tasks, and reminders. The tasks page shows you a list of tasks and has a button. The button when clicked shows you a modal that you can use to add a task. The reminders page shows you a list of reminders and has a button with similar functionality. The tasks page is the default page, so when you open the app, that is the first page you see.
Single-Page Application (SPA)
The drawbacks of SPA
Server-Side Rendering (SSR)
So, unlike in SPAs, when we use SSR, the browser will be able to immediately show the tasks page. This reduces the time taken for FCP and consequently, vastly improves the user experience. Moreover, since it is the server that fetches the task list, the user’s internet speed is going to be immaterial. And the server can fetch the data a lot faster because the request goes through a private network.
The drawbacks of SSR
Additionally, once the browser loads the initial page, then this app is going to behave like a SPA. So, if the user navigates to the reminders page, then the browser is going to render this page and send a request to fetch the list of reminders. On that account, the only real advantage of SSR comes at the initial page-load stage with the FCP score, while TTI is going to be almost the same.
React Server Components (RSC)
RSC resolves these issues by incrementally streaming the content of the app from the server to the browser. Let’s see how this happens. When the user loads the app, the browser is going to send a request to the server. Just like what happens with SSR, the server is going to fetch the data from the backend. But we don’t need to wait till we get the data from the backend. There are parts of the app like the header and the side panel that we can render without the data. So, the server renders these components and streams them to the browser.
SSR vs. RSC
Since the server does not wait for the data, the FCP is going to be a lot faster in RSC than SSR. And once the server fetches the data from the backend, it can render the list and stream it to the browser. The browser can deserialize it and add it to the React component tree.
Besides, unlike in SSR, in RSC, server-side rendering does not take place during the initial page load only. Let’s examine this in detail. In RSC, there are two types of components—server components and client components. Server components, as the name implies, are rendered in the server and the browser renders the client components. We can define any component that doesn’t involve user interactivity like a mouse click or keyboard input, and use React hooks like the useState hook and the useEffect hook as a server component. The server can render these components and stream them to the browser.
Now, what happens when the user navigates to the reminders page? Since the reminder list involves no user event and doesn’t use any React hooks, it can be a server component. So, the server can fetch the data, render the list and stream it to the browser, along with the code for the ‘Add Reminder’ button. As a result, unlike with SSR, we can offload all data fetching to the server.
The benefits of RSC