Unlocking the Power of React Server Components | Part 1

hayk_sargsyan_d4ff5b456d3

Hayk Sargsyan

Posted on November 26, 2024

Unlocking the Power of React Server Components | Part 1

👋 Hi, everyone!
React.js is one of the most popular JavaScript libraries for building user interfaces. While the React community well-documented, there are still some lesser-known themes and concepts.
Let's make tea or coffee, let's go!
React Server Components (RSC) are a new experimental feature introduced by the React team to optimize rendering performance. They allow developers to build parts of their app that are rendered on the server while still maintaining the React component model.
It's running in a separate environment from client or SSR server, and can be called once at build time on CI server, or they can be run for each request using a web server.
With power of React Server components we can read file content right in the our React component.
Below, there are a simple example, how can we do it.

import marked from 'marked'; // Not included in bundle
import sanitizeHtml from 'sanitize-html'; // Not included in bundle

async function Page({page}) {
  // NOTE: loads *during* render, when the app is built.
  const content = await file.readFile(`${page}.md`);

  return <div>{sanitizeHtml(marked(content))}</div>;
}
Enter fullscreen mode Exit fullscreen mode

The client will only see the rendered output from the file. This means the content is visible during first page load, and the bundle does not include the expensive libraries (marked, sanitize-html) needed to render the static content.

Server Components with a Server

With Server Components we can communicate with database, fetch any data and use in the client. It's we can do it in Next.js applications too, integrate any ORMs.
Below is a simple example of the usage of Server Components for fetching data from database.

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>
  );
}

async function Author({id}) {
  // NOTE: loads *after* Note,
  // but is fast if data is co-located.
  const author = await db.authors.get(id);
  return <span>By: {author.name}</span>;
}
Enter fullscreen mode Exit fullscreen mode

In database file there can be implementation of data query from database.
For example:

const db = {
  notes: {
    get: async (id) => {
      return dbClient.query('SELECT * FROM notes WHERE id = ?', [id]);
    }
  },
  authors: {
    get: async (id) => {
      return dbClient.query('SELECT * FROM authors WHERE id = ?', [id]);
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

The bundler then combines the data, rendered Server Components and dynamic Client Components into a bundle. When the page loads, the browser does not see the original Note and Author components; only the rendered output is sent to the client. Server Components can be made dynamic by re-fetching them from a server, where they can access the data and render again.

The power of Async Components with Server Components

Server Components introduce a new way to write Components using async/await. When you await in an async component, React will suspend and wait for the promise to resolve before resuming rendering. This works across server/client boundaries with streaming support for Suspense.
The example of Server Component:

// Server Component
import db from './database';

async function Page({id}) {
  // Will suspend the Server Component.
  const note = await db.notes.get(id);

  // NOTE: not awaited, will start here and await on the client. 
  const commentsPromise = db.comments.get(note.id);
  return (
    <div>
      {note}
      <Suspense fallback={<p>Loading Comments...</p>}>
        <Comments commentsPromise={commentsPromise} />
      </Suspense>
    </div>
  );
}

// Client Component
"use client";
import {use} from 'react';

function Comments({commentsPromise}) {
  // NOTE: this will resume the promise from the server.
  // It will suspend until the data is available.
  const comments = use(commentsPromise);
  return comments.map(commment => <p>{comment}</p>);
}
Enter fullscreen mode Exit fullscreen mode

The comments are lower-priority, so we start the promise on the server, and wait for it on the client with the *use * API. This will Suspend on the client, without blocking the note content from rendering.

In next parts, we will discuss Server Actions and Directives ("use client", "use server") power, and why "use server" doesn't have same role as "use client"🤔

See you soon!

While Server Components are still in the experimental phase, they are slowly gaining attention for their potential to improve large-scale applications. They eliminate the need for unnecessary JavaScript to be sent to the client, which results in faster loading times and a smoother user experience.

💖 💪 🙅 🚩
hayk_sargsyan_d4ff5b456d3
Hayk Sargsyan

Posted on November 26, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related