Proof That React With RSCs is 2X Faster!

By Jack Herrington

TechnologyAI
Share:

Key Concepts

React Server Components (RSC), App Router, Pages Router, Server-Side Rendering (SSR), Client-Side Rendering (CSR), Single Page Application (SPA), Virtual DOM (VDOM), Hydration, Flight Data, use client, Valo (state manager), Data Attributes.

Performance Comparison: App Router (RSC) vs. Pages Router vs. SPA

The video demonstrates a performance comparison between a Next.js application built with the App Router (using React Server Components), the Pages Router (traditional SSR), and a SPA (Client-Side Rendering). The core argument is that, under specific circumstances, RSCs in the App Router can offer superior performance compared to both the Pages Router and a SPA.

Specific Details:

  • App Router (RSC): Initial render time of 25.4 milliseconds (artificially slowed down machine).
  • Pages Router: Initial render time of 54.9 milliseconds (artificially slowed down machine).
  • SPA: Initial render time of 58 milliseconds (artificially slowed down machine).

Key Points:

  • The App Router version leverages React Server Components, which render on the server and send a serialized VDOM (flight data) to the client.
  • The Pages Router renders on the server but requires re-running all components on the client for hydration, leading to a performance overhead.
  • The SPA sends a blank page and all JavaScript to the client, requiring the entire application to be rendered from scratch, resulting in a performance penalty.

App Router (RSC) Architecture and Workflow

Step-by-Step Process:

  1. Server-Side Rendering: The server renders the React Server Components, creating a VDOM.
  2. HTML Generation: The server generates HTML from the VDOM for initial display.
  3. Flight Data Serialization: The server serializes the VDOM into "flight data," a snapshot of the DOM.
  4. Client-Side Delivery: The server sends the HTML, JavaScript bundles (for client components), and flight data to the client.
  5. Client-Side Hydration:
    • The client boots the React application.
    • The client initializes the VDOM using the received flight data.
    • Only client components (marked with "use client") are rendered on the client.
    • The client reconciles the VDOM with the actual DOM. If there are no differences, no DOM manipulation is needed.

Technical Terms:

  • Flight Data: A serialized representation of the VDOM sent from the server to the client. It allows the client to initialize the VDOM without re-rendering server components.
  • Hydration: The process of initializing the client-side React application with the server-rendered HTML and data.

Pages Router Architecture and Workflow

Step-by-Step Process:

  1. Server-Side Data Fetching: getServiceSideProps fetches data on the server.
  2. Server-Side Rendering: The server renders the components using the fetched data.
  3. HTML Generation: The server generates HTML from the rendered components.
  4. Client-Side Delivery: The server sends the HTML, JavaScript bundles, and the data fetched by getServiceSideProps to the client.
  5. Client-Side Hydration:
    • The client boots the React application.
    • The client initializes the application with the getServiceSideProps data.
    • All components are re-rendered on the client.
    • The client reconciles the VDOM with the actual DOM.

Key Issue: The re-rendering of all components on the client is the primary performance bottleneck in the Pages Router.

SPA Architecture and Workflow

Step-by-Step Process:

  1. Blank Page Delivery: The server sends a blank HTML page and all JavaScript bundles to the client.
  2. Client-Side Rendering:
    • The client boots the React application.
    • The client fetches data (if needed). In the example, the data is included in the initial bundle for a performance advantage.
    • All components are rendered on the client from scratch.
    • The client creates all DOM nodes.
    • The client reconciles the VDOM with the (initially empty) DOM.

Key Issue: The need to create all DOM nodes from scratch and render all components on the client leads to a performance overhead.

Implementing Client-Side Interactivity with RSCs

The video addresses the challenge of adding client-side interactivity (e.g., "Add to Cart" buttons) while maintaining the performance benefits of RSCs.

Solution 1: Using "use client"

  • Mark the interactive component (e.g., the "Add to Cart" button) with "use client".
  • This designates the component as a client component, which will be rendered on the client.
  • Trade-off: This approach slightly increases the initial render time (e.g., from 25ms to 26.5ms in the example) but provides full client-side interactivity.

Solution 2: The "HTMX/jQuery Hack" (Data Attributes and Event Handlers)

This is presented as a trick to keep components as RSCs while still enabling interactivity.

Step-by-Step Process:

  1. Remove "use client": Remove the "use client" directive from the component.
  2. Attach Data to the Element: Use HTML data attributes (data-*) to attach the necessary data (e.g., product information) to the button element.
  3. Create a Client-Side Event Handler: Create a client-side function (marked with "use client") that acts as an event handler. This function must accept an event object as its argument.
  4. Extract Data from Data Attributes: In the event handler, extract the data from the data attributes of the button element.
  5. Trigger the Action: Use the extracted data to trigger the desired action (e.g., adding the product to the cart).

Example:

// AddToCartButton.tsx (without "use client")
<button data-id={product.id} data-name={product.name} onClick={onAddToCart}>Add to Cart</button>

// onAddToCart.ts ("use client")
import { addTocart } from './cart';

const onAddToCart = (event: React.MouseEvent<HTMLButtonElement>) => {
  const button = event.currentTarget;
  const id = button.dataset.id;
  const name = button.dataset.name;
  // ... extract other data attributes
  addToCart({ id, name, ... });
};

Explanation:

  • By attaching data to the element using data attributes, the RSC can pass the necessary information to the client-side event handler without being a client component itself.
  • The client-side event handler is responsible for extracting the data and triggering the desired action.

Caution: This hack might be less maintainable and more complex than simply using "use client" for interactive components.

State Management with Valo

The video uses Valo as an external state manager for managing the cart state.

Key Points:

  • Valo is presented as an alternative to Redux or Zustand.
  • It allows for easy state mutation by directly modifying the state object (similar to MobX).
  • The useSnapshot hook is used to access the current state of the cart in the Cart component.
  • Using an external state manager allows leaf nodes in the component tree to easily access and modify the cart state without relying on context or prop drilling.

Conclusion

React Server Components, when used appropriately, can offer significant performance advantages over traditional SSR (Pages Router) and SPA approaches. The key is to leverage server-side rendering for static content and minimize the amount of JavaScript that needs to be executed on the client. While adding client-side interactivity requires careful consideration, techniques like using "use client" or the data attribute hack can help maintain performance while providing a rich user experience. The choice of state management library (e.g., Valo) can also impact the overall architecture and performance of the application.

Chat with this Video

AI-Powered

Hi! I can answer questions about this video "Proof That React With RSCs is 2X Faster!". What would you like to know?

Chat is based on the transcript of this video and may not be 100% accurate.

Related Videos

Ready to summarize another video?

Summarize YouTube Video