Universal Multi-Page React App

If you’re using one of the modern client-side javascript frameworks, I’m sure you’ve heard of server-side rendering. That is, sending the initial markup fully-rendered on the initial page load. This lets the browser start rendering the HTML as the client javascript is being downloaded and executed. It also lets search engines crawl your site easier.

Apps that use this technique often use the React framework and are served using a Nodejs server, and are referred to as “universal” (or “isomorphic”, though I think that term has fallen out of fashion). Universal as in the same React view components are rendered in javascript on both the client and the server.

There are many different ways to go about building a universal React app, with many example projects easily searchable on GitHub and elsewhere. While most of these projects result in single-page applications, I’m going to show you how to build a multi-page universal app using a modern (at time of publication) web server (koa 2), build tooling (gulp 4, webpack 3, babel 6), and build features (incremental builds, production-ready builds, etc.).

Why Multi-Page

Before we get started, while I don’t want to turn this blog into an all-out defense of multi-page web applications, I do want to explain a couple benefits:

Single-page applications have made great strides over the past years to improve performance using code-splitting and whatnot, so it’s really a personal choice.

Universal Basics

I’m not meaning for this to be a detailed explainer of how universal apps work in general. If you’re unfamiliar with the details of how they work, there are several detailed guides on the subject, with the caveat that they’re usually fully-featured from the get-go (with Redux and React-Router) and, thus, complex. As a basic recap, here’s the basic operation:

Build

Server

Client

Multi-Page Modifications

Our multi-page app follows the same structure, but generalizes each step by parameterizing the view aspect, and creating a route helper to prevent code duplication. For our example app, we’ll assume each view ending with *Page.jsx is one of the pages we’ll want to use.

Build

Server

While this example uses koa as the http framework, most frameworks allow you to run such code in the route processing chain (middleware), so this can be easily ported to express or hapi.

Incremental Build

As your application grows and matures, you may end up with several dozen pages composed of many more small components. When a component changes, we don’t necessarily need to rebuild every page, and incur the build time penalty that comes with that. The included gulp build tooling includes a mechanism to only rebuild the pages that include a view that has changed. It does this by:

Going Further

While most boilerplate projects are more fully-realized, this is purposefully devoid of:

These features, while essential for building a functional, production-ready application, can be implemented several different ways, and outside styles, don’t touch this project’s files.

All code can be found on GitHub. Feel free to as me any questions there, file any issues if something isn’t clear, or submit a pull request.