React as a Static Site Generator

Two years ago I converted my website from WordPress to a static build process. It has served me well but the final process was rather messy. Hacks and plumbing to get Metalsmith plugins working my way didn’t helped.

Time for a new project!

Abstract: rebuild my website using React as the template engine for a bespoke static site generator. Learn more Node and ES6 along the way.

Rationale: because I can. 

Result: If you’re reading this I have succeeded.

Why React?

In January I wrapped up a long contract to build a React/Redux web app. I found React to be an intuitive solution to manage UI. React fits nicely with the modular thinking I apply when coding HTML & CSS. I’m aware of existing static site generators that use React. I decided to roll my own for the fun of it.

Writing the React Components

I got off to a false start. Last year’s redesign project was a bit rushed in development and I wanted to fix some issues. It was a mistake to attempt to refactor HTML & CSS whilst translating everything into JSX. I got lost in a refactoring tunnel.

Restart. Step one: write components with existing markup. Step two: improve modularity once my site is rendering (still to do as of writing this).

Example code for my React components

All my React components are functional/stateless. There is no logic to them because I’m don’t plan to render client-side. I don’t need to worry about an API serving data to the browser. My build script parse data and pass it along to React properties once to render HTML.

Some components — e.g. the Bio[graphy] — load default props from a JSON file. Lazier components have data hardcoded in the HTML (Newletter for example). When I get time I’ll do a proper job abstracting these. It’s not an urgent task because I doubt I’ll ever need more than one instance.

dbushell.com build process data flow

The Blog component is an interesting one. It displays recent posts and appears on all pages. It too loads JSON. I have a task to update this file before rendering (rather than passing new props from the parent). The reason for that is incidental, but it does allow pages to render with up-to-date content without parsing all of the blog data again.

I’m storing page content like blog articles as Markdown with YML front matter. A result of exporting WordPress to a format Metalsmith liked. This has proven good enough and I see no reason to change.

An improvement I’ve made is to render syntax highlighting rather than doing it in the browser. I’m using Marked and Prism for that. Components receive HTML content as a property and use the aptly named dangerouslySetInnerHTML.

marked.setOptions({
  smartypants: true,
  langPrefix: 'language-',
  highlight: (code, lang) => Prism.highlight(code, Prism.languages[lang])
});

/**
 * Reduced example from a React component. The `html` prop has been
 * passed through Marked with Prism.
 */
render() {
  const html = () => {
    return {__html: props.html};
  };
  return (
    <div className="post" dangerouslySetInnerHTML={html()}/>
  );
}

This seems like an acceptable solution. I see no reason to convert the inner content to React elements only to render back to HTML immediately.

I render everything inside the body tag with React. I’m using Handlebars to glue together the final page. This allows me to inline CSS with header/footer partials. It’s simpler and less fussy about formatting. For the same reason I’m also using Handlebars to build my RSS and Sitemap XML files. This avoid any workarounds for namespaced attributes.

Hello GitHub Pages

I’ve been hosting personal websites on a VPS for a long time. The VPS was useful, if not a bit overkill, for WordPress hosting. I first bought it to experiment with Node services, NGINX, and Varnish caching. More recently — in fact for almost two years now — it was doing nothing but hosting static files.

Time to be frugal. Why pay VPS prices when GitHub Pages is free?

For convenience my website exists within two repositories. The source code and the static build. I have the latter repo cloned as a directory within the former (but ignored by Git). This way when I run my build task the static build repo is the destination.

In regards to performance, GitHub Pages does a decent job. There’s some caching and CDN stuff I need to sort out at some point.

Up Next

Now that I’ve rebuilt my website from stratch and it’s indistinguishable from itself prior to doing this work, I plan to:

  • Refactor modularity and trim down CSS
  • Consider going isomorphic/universal (unlikely)
  • Return to my regular blogging schedule

By the way, if you’re reviewing my build scripts and thinking “what in the world…” — you’re not alone! I’ve had some fun for the sake learning new JavaScript features (async/await in particular). Just know that this is not code I’d deliver to a client!

Buy me a coffee! Support me on Ko-fi