React Streaming and Server Side Rendering (SSR)

With this experiment, we are making a significant change in the way you think about, and build Redwood apps. Redwood now becomes SSR-first, and moves away from the Jamstack model of deploying static web assets to a CDN. Your routes are streamed from the server, then hydrated on the client — leveraging React 18’s new streaming & Suspense capabilities.

Part 1: Let’s start rendering on the server + dynamic <meta> tags for SEO and link sharing

What you’ll be able to do

:white_check_mark: Dev, with streaming + SSR
:white_check_mark: Build, then deploy a prod server
:white_check_mark: Dynamically generate HTML <meta> headers, so that when you share your links on Discord/Twitter/Slack - you can generate link previews

Make sure you’re on the canary version of Redwood (v7.x-canary)

yarn rw upgrade -t canary

And now, let’s setup your project for streaming!

yarn rw exp setup-streaming-ssr

This will create a few files:

  • (overwrite) web/src/entry.client.tsx
  • web/src/entry.server.tsx
  • web/src/components/Document.tsx
    • Note that this now becomes your “index.html”. So if you have any customisations there, you might want to bring them in here.
    • While SSR-Streaming is experimental, please don’t remove the index.html file in web/src/index.html - as this is still required.

It will also add a flag to your redwood.toml that’ll tell Redwood internally that we need to build and run your project differently.

:tada: Tada, that’s it!

Now when you:

  • Run yarn rw dev — it will server-render your pages
  • If you want to run a built version:
    • yarn rw build process will build your project ready for server rendering
    • yarn rw serve will run a web server (along with your usual api server)
  • You can use the meta route hooks (see section below)

Additional Docs

  1. The meta routeHook
  2. Accessing API side from Routehooks

What’s next?

In the next few days, I’ll add a follow up post with Part 2, which brings some really exciting new features:

  • Cells being rendered on the server
  • Ability to generate dynamic meta headers using the results in a Cell
  • CSS-in-JS support (libraries like styled components, emotion) [untested]
  • “Render-as-you-fetch” with Apollo client — to give your app a serious performance boost!
1 Like

Part 2: Rendering Cells on the Server

This is where you start to see more benefits of server rendering+streaming, as we make use of React 18’s Suspense architecture, allowing you to “render-as-you-fetch”. This is very alpha and the steps are likely to change.

What you’ll get
:white_check_mark: Cells Rendering on the server
:white_check_mark: Ability to set dynamic meta tags using MetaTags component (in addition to route hooks) - off the back of a Cell query
:white_check_mark: Stream results in of your Cell, as they’re resolved i.e. render-as-you-fetch. If you have multiple Cells (or indeed if you directly use useSuspenseQuery or useReadQuery) - your pages should feel significantly faster on the first render.

Assuming you’ve setup up part 1 already, there’s 3 more steps:

  1. Swap your ApolloProvider

in your web/src/App.tsx let’s swap where the ApolloProvider is imported from

import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
- import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
+ import { RedwoodApolloProvider } from '@redwoodjs/web/dist/apollo/suspense'

/* .... other imports */

const App = () => (
  <FatalErrorBoundary page={FatalErrorPage}>
    <RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
        <RedwoodApolloProvider useAuth={useAuth}>
          <Routes />
/*...... */

This will enable your Cells to render under Suspense.

  1. Now let’s install the experimental apollo client that we need for streaming and ssr

Although it says “NextJS” - the specific version is an experimental build specific to Redwood while we work out all the details with the Apollo team :slight_smile:

yarn workspace web add @apollo/experimental-nextjs-app-support@0.0.0-commit-5141fa5

Note the version number 0.0.0-commit-5141fa5 is important!

:checkered_flag: Et voila. You’ve succesfully enabled streaming+ssr capabilities for your Cells!

Some Gotchas!

  • If you are using queryResult in your Cells, you may notice API changes here. This is still in flux, and changing day-to-day: regardless, please let me know if you were expecting APIs here that you can’t implement any other way
  • Auth currently does not work with server side rendering, however your app will automatically revert to client side rendering, if any queries fail to render on the server
  • Under the hood, Cells now fetch with useBackgroundQuery and useReadQuery - this enables Suspense and “render-as-you-fetch” - but it does change how you reason about some of the lifecycle.

What’s next?
I’ll post some videos next week or so, showing some of the cool things you can achieve with Suspense enabled cells, and streaming them!


Hey Danny,

Thanks for all your work on this. I’m about to start another a new redwood project. Would you take the the leap and use SSR or play it safe and stick with old school RW for now?

Thanks for all your work on this. I’m about to start another a new redwood project. Would you take the the leap and use SSR or play it safe and stick with old school RW for now?

Hi Shan, it depends on what your appetite for dealing with changes are. While SSR+Streaming is in the experimental stage, we will make changes under hood and we cannot guarantee that function signatures, etc. won’t change - it won’t be part of semantic versioning, so sometimes we will break things even in minor versions/patches.

That being said, all you have to do to switch the experimental.streamingSsr flag in your redwood.toml, and switch the Apollo provider to toggle between stable and experimental.

We are grateful for any feedback in the experimental stage, and it would be your chance to shape these features (and call out missing ones) - so when this does become stable, you’ll be among the first to ship with these benefits without needing to adjust your code!

1 Like