Welcome to the EpicWeb.dev Workshop app!

This is the deployed version. Run locally for full experience.

React Server Components 🀹

Intro

πŸ‘¨β€πŸ’Ό Hello, my name is Peter the Product Manager. I'm here to help you get oriented and to give you your assignments for the workshop!
Today we're going to implement a React framework based on React Server Components and Actions! When we're finished with this, you'll have a deep understanding of the primitives that power React Server Components and Actions which is the future of React.
We're using unpublished versions of some react packages for this workshop. We'll be dealing with implementation details that are not part of the public React API and could change. But the concepts you're learning (which will be our foucs) will stick around forever.
We'll be building this framework without any build tools. This means no TypeScript, no JSX, no Vite, nothing. We're going to be writing plain JavaScript and using Node.js/browser native APIs to build our framework.
While this isn't entirely ergonomic, it will help you separate what React is offering us from the tools we use to build with React.
The goal is for you to understand RSCs free of any abstractions so when you go to a tool that supports RSCs, you'll have a deep understanding of what's going on under the hood.
One of the challenges you're going to have to face is understanding the difference between the requirements of React Server Components and Actions vs our own choices in this specific implementation of RSCs. RSCs are a very low-level primitive and there are lots of ways to accomplish the same thing. Different implementations will have different trade-offs. Try to focus on the overall concepts and not get too bogged down in the specifics of this implementation.
Because we're working with such primitive APIs, this will be one of the more challenging things you've done with React. Keep in mind that frameworks exist which enable you to offer an awesome user experience with React. This workshop is about understanding the primitives that power those frameworks now and in the future.

What are React Server Components?

Let's take a look at a flowchart showing a typical Single Page Application (SPA) built with React:
A flowchart for a Typical SPA as described below
Here's a bullet-point text version of this flowchart:
  • User goes to site
    • Browser requests document
      • Server responds with document
        • Browser renders loading spinner
    • Browser requests client code
      • Server responds with client code
        • Browser updates UI components
    • Browser requests data
      • Server generates data response
        • Browser sends JSON
          • Browser updates UI with JSON data
  • User triggers state change (i.e., route change)
    • Browser renders pending UI
    • Browser requests new data
      • Server generates data response
        • Browser sends JSON
          • Browser updates UI with JSON data
  • User triggers action (i.e., form submission)
    • Browser renders pending UI
    • Browser makes POST request
      • Server performs action with POST body
        • Browser sends JSON
          • Browser updates UI with JSON data
React server components (RSCs) are a new feature in React that allows you to stream React components from the server to the client. This means that instead of requesting data from the server and then rendering it on the client, you can make a request, have React generate the UI on the server, and send it back to the client.
A flowchart for React Server Components and actions as described below
Here's a bullet-point text version of the flowchart:
  • User goes to site
    • Browser requests document
      • Server responds with document
        • Browser renders Suspense fallback
    • Browser requests JSX payload
      • Server generates Serialized JSX with react-server-dom-esm/server.renderToPipeableStream
        • Server streams Serialized JSX
          • Browser renders streamed UI with react-server-dom-esm/client.createFromFetch
            • Browser requests client component code
              • Server responds with client component code
                • Browser hydrates client components
  • User triggers state change (i.e., route change) NOTE: This only applies once data starts streaming. Until then, state transitions act like a typical SPA.
    • Browser renders pending UI with startTransition
    • Browser requests JSX payload
      • Server generates Serialized JSX with react-server-dom-esm/server.renderToPipeableStream
        • Server streams Serialized JSX
          • Browser updates streamed UI with react-server-dom-esm/client.createFromFetch
  • User triggers action (i.e., form submission)
    • Browser renders pending UI with useActionState
    • Browser makes POST (via callServer)
      • Server determines action and call with parsed request body
        • Server streams Serialized JSX and action return value
          • Browser updates streamed UI with react-server-dom-esm/client.createFromFetch
There are very subtle differences in the flowchart above, but the impact of the differences is profound.
As a result of this architecture, no code for that UI needs to be sent to the client and the data doesn't even need to be sent to the client either. This solves some pretty significant performance and maintainability challenges with SPAs or even server rendered apps.
We'll be building this framework as a SPA which uses RSCs on the server. You can absolutely enhance this further to support server rendering if you like, but we're going to be skipping that optimization to keep things simple.
Let's go!