Server Rendering

Explanation

Server rendering with Curi is pretty similar to client side rendering. The server should have a catch all route handler that will respond to all (non-static file) requests.

// express
import { createReusable } from "@curi/in-memory";

// 1. Create a history function
let reusable = createReusable();

function catchAll(req, res) {
  // 2. Create a router using the current location
  //    and the root React routing component
  let router = createRouter(reusable, routes, {
    history: { location: req.url }
  });
  let Router = createRouterComponent(router);

  // 3. Wait for the response to be generated
  router.once(({ response, navigation }) => {
    // 4. Generate the HTML markup by rendering the <Router>
    let markup = renderToString(
      <Router>
        {renderFunction}
      </Router>
    );
    // 5. Insert the markup into the page's html and send it
    res.send(renderFullPage(markup));
  });
}

app.get("*", catchAll);

The above example is very basic. Some other things that you might need to consider are:

  • Data loading — You would need to maintain two copies of your routes if you want to handle data fetching on the server differently than it works on the client side. This is not something that I have explored very closely yet, so I don't have any recommendations on exactly how to approach this.
  • Code splitting — In order to use dynamic imports on the server, you will probably need to use a Babel plugin like dynamic-import-node. Unfortunately, dynamic-import-node breaks Webpack's code splitting. In order for your code to be split into multiple bundles, you should ensure that dynamic-import-node isn't being run when building your client side bundle. The solution used in this experiment is to use the env property.
    {
        "presets": [ "es2015", "react" ],
        "plugins": [
          "syntax-dynamic-import"
        ],
        "env": {
          "server": {
            "plugins": ["dynamic-import-node"]
          }
        }
      }

    Then, when starting the server, make sure that BABEL_ENV=server.

    cross-env BABEL_ENV=server npm start

On GitHub

If you want to run this code locally, the source code is available on GitHub.