React Server Components (RSC)

How to Setup Redwood Experimental React Server Components

:warning: Experimental Code with Limited Functionality

This is the bleeding edge of Redwood development. Proceed accordingly.

CURRENT FUNCTIONALITY and EXPECTATIONS

  • āœ“ You can render one or more client components inside the main App server component
  • āœ“ CSS in external .css files is supported
  • āœ“ CSS Modules are supported
  • ⤫ Right now there is no router support. So there’s only a main App.tsx server component.
  • ⤫ Last time we tried to use React Server Component libraries (npm packages) they didn’t work. So probably still don’t.
  • ⤫ There is no yarn rw dev equivalent. You have to build and serve. And you have to do that manually for every change you make. No automatic reloading.

:dart: Code: RSC PRs

RSC-related PRs target the main branch; a @canary is published for each merged PR

Merged RSC PRs
Open RSC PRs

Walkthrough

Setup Instructions

(:video_camera: These are the same steps as described in a video above, so if you rather watch than read, the video is for you :video_camera:)

1. Create a new project

Redwood uses yarn, but yarn create doesn’t support specifying the version (@canary), so for this command only we’ll use npx

npx -y create-redwood-app@canary -y ~/tmp/rw-rsc
cd ~/tmp/rw-rsc

2. Enable relevant experiments

yarn rw experimental setup-streaming-ssr -f
yarn rw experimental setup-rsc

3. Build

yarn rw build -v

4. Serve

yarn rw serve

Things to Try

After following the setup instructions above, you should have an app up and running that looks like this
image

You can try adding another <Counter /> client component to see that React state hooks works as expected.

3 Likes

Last time we tried to use React Server Component libraries (npm packages) they didn’t work. So probably still don’t.

are you setting the react-server condition anywhere? i think i had an issue where i was applying the condition to user code but not external packages or my react overrides. that was in webpack though

(easiest way to check is npm i server-only and then import 'server-only'; in RSC code. if that works you’re good)

@lubieowoce Thank you so much for jumping in and offering suggestions :pray:

Right now I’m launching the RSC server like this

execa(
  'node',
  ['--conditions react-server', './node_modules/@redwoodjs/vite/dist/runRscFeServer.js'],
  {
    cwd: getPaths().base,
    stdio: 'inherit',
    shell: true,
  }
)

I hope that’s enough, but I’ll double check with the server-only package :slight_smile:

idk your setup, but if you’re running Vite over RSC code (from a quick look, I think I saw a rscIndexPlugin() somewhere?) then you probably need to tell Vite to use that condition too – that obv affects how it’ll resolve imports of things like server-only

@lubieowoce You were right all along. There’s a bug with Vite where it doesn’t care about the conditions (like react-server) that you pass into it when SSR loading modules

Some exciting new developments! Redwood now supports React Server Functions aka React Server Actions :tada:

Server Functions is a way for client components to directly call functions on the server. For this to work the server function needs to be placed in a separate file with a 'use server' directive at the top.

It can look like this

'use server'

// module state on server
let counter = 0

export const getCounter = () => counter

export const increment = () => {
  counter += 1
}

Note that this is just a simple example - not best-practice. counter will be shared by everyone!

Pressing ā€œIncrement server counterā€ in the client component will update counter on the server and re-render the page.

Here’s the code for that client component

'use client'

import { useState } from 'react'

import { mutate } from '@redwoodjs/vite/client'

interface Props {
  increment: () => void
}

export const ServerActionCounter = ({ increment }: Props) => {
  const [count, setCount] = useState(0)
  return (
    <div style={{ border: '3px blue dashed', margin: '1em', padding: '1em' }}>
      <h3>This is a client component.</h3>
      <p>Count: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
      <p>
        <button onClick={() => mutate(() => increment())}>
          Increment server counter
        </button>
      </p>
    </div>
  )
}

And here’s how it’s used:

import { getCounter, increment } from './funcs.js'

// ...

return (
  <>
    <p>Server counter: {getCounter()}</p>
    <ServerActionCounter increment={increment} />
  </>
)

I’ve you’ve tried Server Actions in Next you should know that the RW implementation is still very limited. For example you can’t yet add use server inside a function to make it a server action. You can only make an entire file a ā€œReact Server Functions-fileā€.

4 Likes

Redwood’s RSC implementation now supports using React Server Functions/Server Actions as form actions. I.e. <form action={onSendServerAction()}>. I’ve also made it support importing npm packages that have react components that uses the 'use client' directive.

Here’s a video demonstrating both those features by calling ChatGTP to build an AI chatbot:

It uses https://www.fixie.ai’s ai-jsx package to call the chatgpt api in an RSC.

1 Like

I’ve created a bunch of demo repos for RSCs, including the AI chatbot one from the post above (do note that you need to add your own OpenAI API key to .env for that one to work)

Here’s the list

  1. GitHub - Tobbe/rw-rsc-client-counter: Initial RW RSC demo with 'use client' Counter component
  2. GitHub - Tobbe/rw-rsc-suspense: RW RSC Demo showing Suspense
  3. GitHub - Tobbe/rw-rsc-server-mutation: RW RSC demo that mutates server state
  4. GitHub - Tobbe/rw-rsc-rsf-return-value: RW RSC demo that asynchronously gets a value from a server function
  5. GitHub - Tobbe/rw-rsc-form-server-action: RW RSC Demo showing a form action
  6. GitHub - Tobbe/rw-rsc-ai-jsx: RW RSC demo that integrates with ai-jsx

And I also added GitPod buttons to all the demo READMEs, so you can try them out really easily without setting anything up on your own computer!

Here’s an example for the first demo (rw-rsc-client-counter)

Open in Gitpod