An Almost Static Stack

Or how create-react-app, with a couple of modern tools, can make building static sites (with benefits) a breeze.

Update, 17 November 2017: The response to this setup has been wild, and super cool. My sincere thanks to everyone who has helped people out getting set up in the responses, and to those who have reached out and said nice things.

I have updated the dependencies in the repo to the latest versions, along with the guide, and tested that everything is still working as expected. Some of the output in your terminal might look slightly different than the screengrabs, but do not fear—you’re all good! And again, thanks for reading.


Note to self: write more notes to self

I’m a big fan of create-react-app. It hugely simplifies starting a new React-based project, and it serves pretty well for any ES6 / ES2015 setup regardless of whether you use React. I originally built Sandpit with it, purely because I could get straight to coding, writing tests, and deploying to a static site host like Surge or Now.

create-react-app’s best feature is its simplicity, but that that simplicity comes at a cost. As your application grows in complexity, you either have to yarn eject and take the entire set up into your own hands, or live with the limitations.

But hey, neither of those options are necessarily bad. But if you’d rather stick within create-react-app, there are a few tools you can use to take advantage of the incredible ease of use and speed of development that comes with create-react-app, without any terrifyingly massive webpack.config.js files keeping you up at night.


So, what are they?

There are five ingredients to this delicious paella of web development joy.

create-react-app
Kind of goes without saying, really.

react-router
Declarative routing for React, with a fancy new v4 that resolves a lot of the confusion around using the library.

react-helmet
Manages the document head, including title, description and meta tags.

react-snapshot
A zero-configuration pre-renderer for React apps.

styled-components
All the cool kids are into it, and it allows you use SCSS-like code in create-react-app (which, out of the box, supports vanilla CSS)—styling for the component age.


Okay, so, why?

One of the challenges of deploying an app built with create-react-app is that it is rendered entirely client side. Although Google can render JavaScript content when crawling a site, it doesn’t always, and there is always the risk that your JavaScript bundle will fail anyway. It’s incredibly worthwhile to send your HTML with the initial packet, which is where everything above comes into play.

When you build your site, react-snapshot fires up a server, visits each relative link, and saves a copy of the rendered page in the build folder. When you deploy it to Surge the app is “hydrated” with the JavaScript hooks, and you can spend the remainder of your afternoon sipping a crisp white wine spritzer (or apple juice and soda, depending on your personal preference).

So, let’s go.


The create-react-app installation process, now with added emojis!

Setting it up

First off, get create-react-app running.

npm install -g create-react-app

Then you can make a new app, which will take a while, so maybe make yourself a coffee? It’ll take the edge off that daytime wine you had.

create-react-app app-name

When the installation completes, jump into the directory and install some fancy dependencies.

cd app-name && yarn add react-router-dom react-helmet react-snapshot styled-components
Also, you 100% do not need to use yarn if you don’t want to. npm is also great, but yarn is super fast, and let’s face it we’re all still reeling from that whole create-react-app installation waiting game. Plus it has emojis!

react-snapshot requires a couple of quick changes to the default create-react-app setup.

First off, change your package.json build option.

"build": "react-scripts build && react-snapshot"

And then patch up the default src/index.js.

import React from 'react';
import { render } from 'react-snapshot';
import App from './App';
import './index.css';
render(
<App />,
document.getElementById('root')
);

And voila. If you run yarn build, you’ll see some sweet snapshot magic.

$ react-scripts build && react-snapshot
Creating an optimized production build...
Compiled successfully.
🕷   Starting crawling http://localhost:2999
✏️ Saving / as /index.html
🕸 Finished crawling.

✨ Done in 11.06s.

As you can see, react-snapshot is taking the app and outputting a static index.html.

It might not look like much, but it’s so great.

Still, it doesn’t do much about the key areas that a search engine is looking at—the title and metadata in the head. That’s where react-helmet comes in. It is a component that helps you manage the document head, meaning while someone navigates your app, your metadata stays in sync.

Here we’re populating the title with “You Are Doing Great”, which is true, using the Helmet component. It displays in your browser tab, and all is well with the world.

Unfortunately, when viewing the source, the <title> is still React App.

ლ(ಠ益ಠლ)

That’s where react-snapshot comes in. By using react-snapshot in conjunction with react-helmet, you can pre-populate both the head of each route, and the body, on server and client side. It is literally win-win. So let’s build it.

yarn build
🕷   Starting crawling http://localhost:2999
✏️ Saving / as /index.html
🕸 Finished crawling.

And when we look at the generated index.html?

<title>You Are Doing Great</title>

Omg, see? Even the server gets it.

For routes that aren’t snapshotted (I don’t think that’s a word, honestly), react-snapshot provides a fallback, 200.html, which is behaves exactly like the non-snapshotted (still not a word) index.html. Hosts like Surge serve it automatically if no other snapshot is found, meaning react-router can grab the URL and route it appropriately. It’s automagical.


I’ve pulled together a repo that shows all this in action, called You Are Doing Great, or YADG. You can see the live version in action here.

The repo is on Github, too:
https://github.com/superhighfives/an-almost-static-stack

It’s set up with all the features I’ve mentioned, including routing, snapshotting, metadata management, almost hot-reloading, and using styled-components.

Also, if you’re looking for an example in the wild, I built my site on, essentially, the same stack.


If you have any questions leave a comment or say hi via Twitter. Alternatively, enjoy a GIF of an owl being petted.

Owls are so great.