Understanding react server components

Understanding react server components

React : Server components

React Server Components (RSCs) are a relatively new feature in React that allow you to build a different kind of component—one that lives and runs entirely on the server. This opens up some exciting possibilities for improving performance, simplifying data fetching, and enhancing security in your React applications.

Official documentation : https://react.dev/reference/rsc/server-components

Table of contents

  1. Introduction

  2. React Server Components to the rescue

  3. Pros and cons

    1. Pros
    2. Cons

Introduction

In most web applications, there are some React components that display data that is fetched from the server. This could be a list of emails you have received, a table rendered based on the data in the backend, etc. Such components need to access a server API to fetch this data and then appropriately render it using markup. Historically, React engineers have relied on the useEffect API to achieve this.


function Note({id}) {
const [note, setNote] = useState('');
// NOTE: loads *after* first render.
useEffect(() => {
fetch('/api/notes/{id}').then(data => {
setNote(data.note);
});
}, [id]);

return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}

This basically requires developers to create an API endpoint. This is problematic and extra work.

React Server Components to the rescue

RSC solves this problem by allowing developers to write components as if they are always rendered on server. This means the developers can directly access the database and use the result to create the markup.

Example:



import db from './database';

async function Note({id}) {
// NOTE: loads *during* render.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}



This component is generated on the server and the end result is sent to the browser as pure markup. Note that whil the browser receives and renders the component, it is not interactive and has no concept of "state" and hence will not change because of state changes. If you want to add interactivity then you will have to use the use client directive to make sure certain parts are built at the client level.


// Server Component
import Expandable from './Expandable';

async function Notes() {
const notes = await db.notes.getAll();
return (
<div>
{notes.map(note => (
<Expandable key={note.id}>
<p note={note} />
</Expandable>
))}
</div>
)
}

// Client Component
"use client"

export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<button
onClick={() => setExpanded(!expanded)}
>
Toggle
</button>
{expanded && children}
</div>
)
}

Pros and cons

Rendering things on server side uses computation resources from the server. This is expensive. It should be done carefully as part of performance optimizations where obtaining data and and sending it to client as API response is deemed less performant.

Pros

  • Server rendered components are much faster
  • Simple programming model where you can directly access database
  • Easier to test and write unit tests for.
  • No need to write a seperate API layer to access data.

Cons

  • Mixing server and client components is going to make your code harder to read
  • When not used judicially might harm end app performance
  • Not very useful when you already have a robust data access api e.g. graphQL.