This is a high level overview of how I've implemented the static site generator with live reloading for this blog using React and Node.js APIs. It does not use Webpack, Browserify, or any other bundling system which I think is overkill for static site generation.
The heart of this static site generator is the writeReactElement()
function.
In its entirety:
const cache = ;const colors = ;const fs = ;const path = ;const ReactDOMServer = ; module { const html = `<!doctype html>`; if html === cacheurl return Promise; cacheurl = html; const outputDir = path; return fs;};
The function takes two arguments, element
which is a React Element, and url
which is the directory under the top level public
directory with which an
index.html
file will be written with the html string returned from calling
ReactDOMServer.renderToStaticMarkup()
.
For example, writeReactElement(<h1>Hello</h1>, '/hello/')
will write
Hello
to public/hello/index.html
.
There's some caching business going on in the implementation, but that's just an optimization detail.
With this, the strategy for generating a static site for a blog is the following:
-
Read every file under the top level
posts
directory withfs.readdir()
. This returns (asynchronously) a list of filenames in the directory. -
Given the filename of a post, read and parse the post. For me, my posts have the filename format
YYYY-MM-DD-slug.md
, and my posts are written in markdown with front matter. Therefore, I use the marky-markdown and front-matter npm packages to parse my posts. -
Create an object
post
representing all data needed to render the post, such as the title, slug, date created, html from markdown, etc., and write it out with; -
Sort the posts by date, then write out the index page containing all posts and their excerpts with
; -
Finally, write out any other static pages, such as an about page, with something like
;
I use babel-register along with Babel's React Preset in order to make my Node.js program understand JSX. All of my React components are stateless functional components. For example, the About page is something like this:
const Layout = ;const React = ; module <Layout active="about" title="About"> <section> <h1>About</h1> <article> Hi this is the about page... </article> </section> </Layout>;
Here is the entire build script that pretty much does the steps listed above. I
have this script run when running npm run buid
:
; const fs = ;const Index = ;const path = ;const postUtils = ;const staticPagesHandler = ;const writeReactElement = ; const postsDir = path; { fs; ;} moduleexports = build; if requiremain === module ;
For the live reloading stuff, I use the technique described in my previous
article, Node.js hot module reloading development. My watch handler just
calls functions that ultimately calls writeReactElement()
with the appropriate
component and props, based on file that was changed. For example, if the
filename matches a file under the posts
directory, then run the
post-changed-handler
which reparses the post and writes out the html.
I have browser-sync set up to watch the public
directory, so the page
automatically reloads when the compiled html files are updated.
And that's the high level overview of my static site generator for this blog. The entire source code is available on Github.