Skip to content
A Suspense enabled React server renderer
Branch: lightyear
Clone or download
Pull request Compare This branch is 50 commits ahead, 422 commits behind facebook:master.
Latest commit 8653bdd Jun 26, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Publish a local release (canary or stable) to NPM (facebook#14260) Nov 23, 2018
.github Update supporting docs (#4) May 4, 2019
examples Add react-apollo-hooks example (#11) Jun 26, 2019
fixtures Don't set the first option as selected in select tag with `size` attr… Mar 22, 2019
packages Bump version to 0.2.0 Jun 26, 2019
scripts Release 16.8.5 Mar 22, 2019
.babelrc Remove ES3-specific transforms (facebook#12716) Apr 30, 2018
.editorconfig Add insert_final_newline to editorconfig Sep 3, 2015
.eslintignore Use Yarn Workspaces (facebook#11252) Oct 18, 2017
.eslintrc.js Refactor ESLint configuration to enable better IDE integration (faceb… Nov 8, 2018
.gitattributes .gitattributes to ensure LF line endings when we should Jan 18, 2014
.gitignore Remove 'flow-coverage-report' script. (facebook#13395) Aug 14, 2018
.mailmap Update .mailmap Sep 6, 2017
.nvmrc Add new docs website (facebook#10896) Sep 28, 2017
.prettierrc.js Add `use strict` to .prettierrc.js (facebook#13787) Oct 20, 2018
.watchmanconfig Added a .watchmanconfig file (facebook#11581) Nov 17, 2017
AUTHORS Update authors for v16 (facebook#10861) Sep 27, 2017 Bump version to 0.2.0 Jun 26, 2019 Update supporting docs (#4) May 4, 2019 Update supporting docs (#4) May 4, 2019
LICENSE Drop the year from Facebook copyright headers and the LICENSE file. (f… Sep 7, 2018 Update Jun 26, 2019
appveyor.yml add nodejs 10 to windows test (facebook#13241) Aug 2, 2018
dangerfile.js Dangerfile exits early if build failed (facebook#14400) Dec 7, 2018
netlify.toml add netlify toml file (facebook#12350) May 20, 2018
package.json Fixed build and added basic Readme Apr 28, 2019
yarn.lock Bump GCC (facebook#14657) Jan 23, 2019

💫 Lightyear

Lightyear is a React server renderer with support for Suspense.

This is a fork of React which reuses most of the code from the official server renderer, extending it with an asynchronous main loop. This enables you to use Suspense to fetch data from any component, without using double-rendering techniques. It also has support for streaming.

⚠️ Warning: Suspense is still unstable, experimental and likely to change and so is this renderer. Use at your own risk!

As soon as there is official SSR-support for Suspense this fork will be deprecated.

While it seems fairly stable it is still early days for this renderer, I need help trying it out! Reporting issues is highly encouraged, as are sharing success stories.


npm install react react-lightyear

If you want to hydrate the markup on the client, you also need react-dom.

Lightyear is currently only tested with React 16.8.6.


Instead of renderToString, use renderToStringAsync which returns a Promise that resolves to the markup.

const React = require('react');
const { renderToStringAsync } = require('react-lightyear/server');
const AppWithSuspense = require('./app');

const markup = await renderToStringAsync(
  <AppWithSuspense />

You hydrate the markup as usual with ReactDOM.hydrate on the client.

Lightyear only takes care of rendering your app to a string, you need to take care of the Suspense parts and de/rehydrating data to the client yourself.

Beside returning a Promise instead of a string and having support for Suspense, this server renderer works just like the official one, meaning you should be able to follow the normal server rendering documentation for any libraries you happen to use.

See also full examples.


How Suspense will work on the server is still undecided/uncommunicated by the React team. A lot of behaviours like how fallbacks will work is currently undefined.

For now, Lightyear stays true to this and tries to avoid introducing behaviours yet to be defined. This means fallbacks from <Suspense>-boundaries are never used on the server. If you need timeout behaviours towards APIs etc, you need to implement them yourself.

Even though fallbacks are currently not used, a <Suspense>-boundary still need to be entirely finished for it to flush to the client when doing streaming server rendering.


Why Lightyear?

Suspense is a very exciting addition to React, perhaps especially for server rendering. Some want to play around with it, some want to explore what future patterns could look like and some want to take a risk using it in production even though it is unstable. It is currently hard to do any of this with server rendering.

Excellent libraries like react-ssr-prepass tackle this by doing an additional pass over the tree to populate the cache before running the normal renderToString on it. I wanted to see if there was a way to avoid the extra rendering pass and also support streaming.

While a new official server renderer is being developed, Suspense might very well be ready on the client first. If this happens, it is my hope that Lightyear can help fill the gap until the official server renderering support is ready.

To be clear, the new official server renderer aims to solve a lot more problems in a much better way than Lightyear aspires to do, it is at best a crude placeholder.

Why the name Lightyear?

The new official server renderer is codenamed Fizz. Fizz->Buzz->Lightyear. Also, like the speed of light, this renderer is blazingly fast! 💫

My previous exploration with a Suspense-ready server renderer was codenamed React Aldrin with a similar motivation.

Why a fork?

I would love to turn this into a contribution at any point, but have heard no such interest from the React team as of yet. This is to be expected, not in the least because so many behaviours for how Suspense should work on the server are still undefined.

I have done my best to make this fork maintainable by extending rather than rewriting the official renderer.

Does React.lazy work with Lightyear?

No. How React.lazy will work on the server is still very much undefined. Compared to the client there are many things that needs to be considered on the server, such as being able to track which chunks has been lazy-loaded in a single render. Lightyear avoids guessing how this will work in the future, instead you are recommended to use any of the existing userland solutions such as loadable-components for code splitting.



  • renderToStringAsync: Promise<String>
  • renderToStaticMarkupAsync: Promise<String>
  • renderToNodeStreamAsync: ReadableStream
  • renderToStaticNodeStreamAsync: ReadableStream


These functions work just as react-dom/server does, so if you are using these, you are probably better off using the official renderer instead!

  • renderToString: String
  • renderToStaticMarkup: String
  • renderToNodeStream: ReadableStream
  • renderToStaticNodeStream: ReadableStream


I welcome any and all contributions.

Right now I especially need help with trying this out in different projects to see how stable it is and find any bugs!

You can’t perform that action at this time.