SSR Flow

This is the final blog on the Inside React series, you can check the previous ones here. React Server Components (RSC) is a powerful feature that allows developers to build modern web applications with improved performance and better user experience. This guide will walk you through the basics of React Server Components, complete with examples to illustrate how they work.

Why Do We Need Server Components?

To understand the need for React Server Components, it’s helpful to look back at the evolution of web development and how it has addressed various challenges.

The World Before React

Before React, web development often involved languages like PHP, which tightly coupled the client and server. In a monolithic architecture, developers could fetch data and render pages directly on the server. While this approach had some benefits, such as simplicity and direct data fetching, it also had significant drawbacks:

Difficulty Scaling: Monolithic applications were hard to scale due to cross-team dependencies and high traffic demands.Maintenance Challenges: As applications grew, maintaining a monolithic codebase became increasingly difficult.

The Rise of React

React was created to address these challenges by introducing a component-based architecture. It enabled developers to build reusable UI components and allowed for incremental adoption into existing codebases. This was crucial for teams, as it facilitated collaboration and composability.

React decoupled the concerns of client and server, making the frontend more flexible. This shift was essential in responding to the growing demand for rich interactivity in web applications.

The Evolution of Rendering Techniques

Over the past decade, web development has seen significant advancements in rendering techniques, from multi-page applications (MPAs) to single-page applications (SPAs), and from client-side rendering (CSR) to server-side rendering (SSR). The goal has always been to serve fast data, offer rich interactivity, and maintain a great developer experience.

What Did Server-Side Rendering and React Suspense Solve?

To understand the need for React Server Components, it’s essential to first grasp the challenges that server-side rendering (SSR) and React Suspense aimed to solve.

Server-Side Rendering (SSR)

SSR focuses on the initial page load by sending pre-rendered HTML to the client, which then needs to be hydrated with JavaScript before behaving as a typical React app. SSR improves the initial load time, but it also has limitations:

All-or-Nothing Waterfall: All data must be fetched from the server before any of it can be shown. All JavaScript must be downloaded before the client can be hydrated, and all hydration must complete before any interaction.One-Time Render: SSR happens only once, when directly navigating to a page.

React Suspense

React Suspense allows for server-side HTML streaming and selective hydration on the client. By wrapping a component with <Suspense>, developers can deprioritize the rendering and hydration of that component, letting other components load without being blocked by heavier ones.

Suspense significantly improved the situation but still left some issues:

Data Fetching: Data for the entire page must be fetched from the server before any components can be shown. Fetching data client-side in a useEffect() hook has a longer roundtrip and happens only after the component is rendered and hydrated.JavaScript Download: All page JavaScript is eventually downloaded, even if streamed asynchronously.Client-Side Hydration: Users cannot interact with components until the client-side JavaScript is downloaded and implemented.Compute Weight: Most JavaScript compute weight still ends up on the client, which could be running on a variety of devices. Offloading this to the server can provide better performance.

What Are React Server Components?

RSCs introduce a new type of component that “runs” on the server and is otherwise excluded from the client-side JavaScript bundle. These components can run during build time, allowing you to read from the filesystem, fetch static content, or access your data layer. By passing data as props from server components to interactive client components in the browser, RSCs maintain a highly efficient and performant application.

Why Use React Server Components?

Improved Performance: By offloading the rendering to the server, the client can receive and display content more quickly.Better User Experience: Faster load times and reduced JavaScript payloads lead to smoother interactions and a better overall user experience.SEO Benefits: Server-side rendering improves SEO because search engines can crawl and index the fully rendered content.

How Do React Server Components Work?

React Server Components work by rendering React components on the server and then streaming the HTML to the client. This means the client receives HTML that is already rendered, reducing the need for client-side JavaScript to build the initial view.

Setting Up React Server Components

we will try to render a react component from a express server.

//React Component
import React from ‘react’;

function App() {
return (
<div>
<h1>Hello, React Server Components!</h1>
<p>This is rendered on the server.</p>
<p>You are awesome!</p>
</div>
);
}

export default App;const express = require(‘express’);
const { renderToPipeableStream } = require(‘react-dom/server’);
const React = require(‘react’);
const App = require(‘./src/App’);

const app = express();

app.get(‘/’, (req, res) => {
const stream = renderToPipeableStream(React.createElement(App), {
onShellReady() {
res.statusCode = 200;
res.setHeader(‘Content-type’, ‘text/html’);
stream.pipe(res);
},
onError(error) {
console.error(error);
res.statusCode = 500;
res.send(‘Internal Server Error’);
},
});
});

app.listen(3000, () => {
console.log(‘Server is listening on port 3000’);
});

The renderToPipeableStream function in React is a part of the React 18+ Server Components feature set. It enables streaming server rendering, allowing for more efficient and responsive rendering of React components on the server side. This function provides a way to generate HTML and stream it to the client, rather than sending a fully-rendered HTML document in one go.

How renderToPipeableStream Works

The renderToPipeableStream function creates a stream that renders a React component tree to an HTML string. This HTML string is then piped to a writable stream, such as an HTTP response. The function takes two main arguments: the React component tree to render and an options object with lifecycle hooks.

Here’s a breakdown of its parameters and usage:

Parameters

React Component Tree: The first argument is the React component tree that you want to render. This is typically created using React.createElement.Options Object: The second argument is an object that includes several lifecycle hooks and configuration options:onShellReady: A callback that is called when the initial shell of the HTML (without any asynchronous content) is ready to be sent to the client.onShellError: A callback that is called if there’s an error while rendering the shell.onAllReady: A callback that is called when all the content (including asynchronous content) is ready.onError: A callback that is called for each error that occurs during rendering.

Fetching Data on the Server

One of the key benefits of React Server Components is the ability to fetch data on the server before sending the rendered HTML to the client. Let’s extend our example to fetch some data from an API and display it.

const express = require(‘express’);
const { renderToPipeableStream } = require(‘react-dom/server’);
const React = require(‘react’);
const axios = require(‘axios’);
const App = require(‘./src/App’);

const app = express();

app.get(‘/’, async (req, res) => {
try {
const response = await axios.get(‘https://jsonplaceholder.typicode.com/posts/1’);
const data = response.data;

const stream = renderToPipeableStream(React.createElement(App, { data }), {
onShellReady() {
res.statusCode = 200;
res.setHeader(‘Content-type’, ‘text/html’);
stream.pipe(res);
},
onError(error) {
console.error(‘Shell error:’, error);
res.statusCode = 500;
res.send(‘Internal Server Error’);
},
});
} catch (error) {
console.error(error);
res.statusCode = 500;
res.send(‘Internal Server Error’);
}
});

app.listen(3000, () => {
console.log(‘Server is listening on port 3000’);
});

Conclusion

React Server Components are a game-changer for building modern web applications. By rendering components on the server and sending the result to the client, you can achieve better performance, improved user experience, and enhanced SEO. This guide covered the basics of setting up React Server Components and provided a simple example of fetching data on the server. With this foundation, you’re well-equipped to start exploring and leveraging the power of React Server Components in your own projects. Happy coding!

I read, write about low level tech details. Please reach out to me for any feedback or tech explorations!

https://www.linkedin.com/in/itherohit/

https://itherohit.dev/

Inside React: Server components was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

​ Level Up Coding – Medium

about Infinite Loop Digital

We support businesses by identifying requirements and helping clients integrate AI seamlessly into their operations.

Gartner
Gartner Digital Workplace Summit Generative Al

GenAI sessions:

  • 4 Use Cases for Generative AI and ChatGPT in the Digital Workplace
  • How the Power of Generative AI Will Transform Knowledge Management
  • The Perils and Promises of Microsoft 365 Copilot
  • How to Be the Generative AI Champion Your CIO and Organization Need
  • How to Shift Organizational Culture Today to Embrace Generative AI Tomorrow
  • Mitigate the Risks of Generative AI by Enhancing Your Information Governance
  • Cultivate Essential Skills for Collaborating With Artificial Intelligence
  • Ask the Expert: Microsoft 365 Copilot
  • Generative AI Across Digital Workplace Markets
10 – 11 June 2024

London, U.K.