Migrating to GatsbyJS Part 1

Abstract: Migrating a static site to Gatsby is pretty simple. I’ll walk through how I did it and the pitfalls along the way.

Gatsby 1.0 was just recently published and I got excited. I’ve been excited about static sites for a while, especially in combination with React. I’ve spoken publicly about building static sites with React and two of my top repositories on GitHub are just for building static sites with React…

Blurred Repos

So I was pretty pumped to see Gatsby hit 1.0. And so far it has not disappointed.

Migrating to Gatsby 1.0

OK, enough context! Let’s migrate something. I chose to start by migrating my personal site since it’s already built with React and is literally 1 page. Here it is—the entire site:

Home page screenshot

Adding Gatsby to an exiting project

Step 1 is to install Gatsby. I’m also adding gatsby-link and the react helmet plugin because I knew I would need them:

yarn add gatsby gatsby-link gatsby-plugin-react-helmet

Create a gatsby-config.js file in the project root and drop the following into it:

// gatsby-config.js
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
},
plugins: [`gatsby-plugin-react-helmet`],
}

Run Gatsby to make sure everything is working.

gatsby develop

This will start a local server. Point your browser to whatever local port it gives you and try it out. You should see a Gatsby 404 page. This is because it expects your source code to be in the src/ directory. In my project all my source is in my client/ directory so Gatsby has nothing to find.

However, when I loaded up the dev server in the browser I was greeted by this hot error 😐 (note, this may well not happen for you):

Gatsby dev server build error

If you do see something like that not to worry. It’s an issue with conflicting versions of react-proxy probably resulting from differing versions of react-hot-loader. You can check this with npm ls react-proxy. Anyway, blow away your node_modules folder and install from scratch. Also check your package.json dependencies to make sure you aren’t depending on any hot loading packages. In fact, now would be a good time to remove almost all your devDependencies from package.json. Gatsby is an entire build pipeline that will take care of most things for you, so consider critically every one of your dev dependencies. After manually pruning mine looked like this:

"devDependencies": {
"babel-eslint": "^7.2.3",
"eslint": "^4.1.1",
"eslint-config-zen": "^3.0.0",
"eslint-plugin-flowtype": "^2.34.1",
"eslint-plugin-react": "^7.1.0"
},

Yup, all loaders and plugins are gone. Gatsby isn’t even in this list because it’s a full-blown dependency, not just a devDependency.

So, once you’ve cleared out your old dev dependencies do a fresh install. Then restart the dev server:

yarn upgrade
gatsby develop

You should see the correct 404 page in your browser.

Moving source code around

Here is the structure of my source code before and after the migration. I’m putting them together here for easy comparison, but I will walk through all of what changed.

Before:

.
├── client
│   ├── components
│   │   ├── App.js
│   │   ├── App.styl
│   │   ├── face.png
│   │   └── favicon.ico
│   ├── lib
│   │   └── vars.styl
│   ├── index.js
│   └── routes.js
├── package.json
├── template.js
├── webpack.config.dev.js
└── webpack.config.prod.js

After:

.
├── src
│   ├── components
│   │   ├── App.styl
│   │   ├── face.png
│   │   └── favicon.ico
│   ├── layouts
│   │   └── index.js
│   ├── lib
│   │   └── vars.styl
│   ├── pages
│   │   ├── 404.js
│   │   └── index.js
│   └── html.js
├── gatsby-config.js
└── package.json

Let’s start with the quick wins:

  • We no longer need webpack config so remove them:
    rm webpack.*
  • Gatsby expects everything under the src directory so let’s make that happen
    mv client src
  • Create layouts and pages directories under src
    mkdir -p src/layouts
    mkdir -p src/pages

Now let’s talk about those directories:

  • src/layouts/: Is where you put wrapper components. Markup that will generally “go around” your individual pages. This usually means headers and footers.
  • src/pages/: This is the meat of a Gatsby site. It’s where the content goes. The rule of thumb is that you should construct your src/pages directory to build your URLs.

For my purposes I had one layout and one index page. Of course I also wanted a 404 page so I created all three. This meant a bit of refactoring since I had everything together in App.js.

I’m migrating from React Router so let’s take a quick look at my route config:

export const routes = (
<Route path=‘/' component={App}>
<IndexRoute component={Home} />
<Route path='*' component={NotFound} />
</Route>
);

Pretty simple right? When migrating over to Gatsby these components had a direct mapping to files in the new structure:

  • App -> layouts/index.js
  • Home -> pages/index.js
  • NotFound -> pages/404.js

Migrating your components will be specific to your project so I’ll just point you to the minimal gatsby example which includes all the relevant files: examples/no-plugins.

CSS and Stylus support

Up until this point I had commented out all my CSS and Stylus imports since I figured they might cause issues. In the case of CSs this fear was unfounded. Gatsby does a great job of handling CSS imports right out of the box.

However I was using Stylus along with CSS Modules which is a bit more complicated. Turns out there was actually zero support for Stylus with Gatsby, but Gatsby is highly extensible so…

Time to write a plugin. I’ll skip over most of the details here since you view the source code of the plugin I wrote directly. But a neat feature of Gatsby is that you create a plugins/ directory in your own project and extend Gatsby right there. This is a really nice developer experience and it makes it easy to write a plugin that can then be contributed back to the Gatsby community later.

So, after all the changes above and creating the stylus plugin my project looked like this:

.
├── plugins
│   └── gatsby-plugin-stylus
│   ├── gatsby-node.js
│   └── package.json
├── src
│   ├── components
│   │   ├── App.styl
│   │   ├── face.png
│   │   └── favicon.ico
│   ├── layouts
│   │   └── index.js
│   ├── lib
│   │   └── vars.styl
│   ├── pages
│   │   ├── 404.js
│   │   └── index.js
│   └── html.js
├── gatsby-config.js
├── package.json
└── yarn.lock

And it worked 🎉.

As for the stylus plugin, there’s currently a pull request to get it merged in. Once it’s merged you can use stylus by just installing the plugin:

yarn install gatsby-plugin-stylus

Final Polish

After getting everything working properly the last step was to add a few things I had neglected while refactoring:

Analytics: I’ve been using Google Analytics on my site all along so I needed to get that working I just needed to add the gatsby-plugin-google-analytics plugin.

{
resolve: 'gatsby-plugin-google-analytics',
options: {
trackingId: 'UA-<MY_TRACKING_ID>',
},
},

Titles and meta tags

react-helmet is great for this and Gatsby recognizes it. Just add the gatsby-plugin-react-helmet and your good to go. See the react-helmet docs for how to actually use it.

Final gatsby-config.js:

// Gatsby
module.exports = {
siteMetadata: {
title: 'Ian Sinnott',
},
plugins: [
'gatsby-plugin-react-helmet',
{
resolve: 'gatsby-plugin-stylus',
options: {
modules: true,
},
},
{
resolve: 'gatsby-plugin-google-analytics',
options: {
trackingId: 'UA-<MY_TRACKING_ID>',
},
},
],
};

That’s all. If you have an questions you can leave a commend or find me on twitter.


P.S. You may have noticed that this was a super simple site (one page!) and that this is “Part 1”. I plan to migrate this blog you’re reading right now to Gatsby and do a Part 2 write up about that. It will be a much more real-world use case of Gatsby and will use the awesome GraphQL infrastructure Gatsby gives you 😋

The importance of Webpack context

Today I ran into an issue testing my react-static-webpack-plugin plugin. I’ve been running webpack builds in subdirectories of the repository and then running test assertions against the output in order to test the project. So far it’s worked great, but after updating my dependencies recently I ran into a nasty error:

Error: Error: Child compilation failed:
Entry module not found: Error: Cannot resolve 'file' or 'directory' ./src/routes.js in /path/to/react-static-webpack-plugin:
Error: Cannot resolve 'file' or 'directory' ./src/routes.js in /path/to/react-static-webpack-plugin

…😖

Not good. But looking at the output and enabling the debug logger in my build process led me to the ultimate culprit, Webpack’s context.

Here’s what was happening:

  • I was running build commands from the root directory of my project
  • The builds I was running needed to be ran in their own subdirectory
  • Building directly in the subdirectory worked fine…
  • Webpack’s context was getting set to the root directory no matter what

After looking at the Webpack docs it quickly became apparent what was happening:

context in the webpack.config.js

The base directory (absolute path!) for resolving the entry option. If output.pathinfo is set, the included pathinfo is shortened to this directory.
Default: process.cwd()

I was pretty sure I knew what process.cwd() would return, so I started to have a hunch. Looking at the Node docs:

The process.cwd() method returns the current working directory of the Node.js process.

Ah, I see. Turns out that my Webpack builds which ran in subdirectories were actually searching for the entry files in the root directory of the project.

The fix was swift and effective. I added the following line to my webpack.config.js files:

context: __dirname

A refreshing dose of minimalism

Today I finished rebuilding my blog using Hexo (see last blog post for details). It’s a blog, so I knew I wouldn’t need too much JS. I started with a blank file and started coding.

Once I was satisfied with the event handling logic I had set up I sat back and reviewed my work. 44 lines including a few comments and zero dependencies. Not too bad. The web has come a long way. CSS was able to take care of a lot of the interactions and animations. But I still wanted to do better.

It occurred to me that I could actually use the :hover state to accomplish all of my open/close menu interactions. Done! A few more lines eliminated. But I still had the focus event handler I was using to select all text in a text box whenever a user put their cursor inside it. I don’t think CSS can do that… so I did end up using JS for this. I took it out of my script file and in-lined it on the input directly.

That was the last event I was handling so now I scrapped my whole JS file with its event logic. Wow! No external JS file. How novel… 😕.

All in all here is the extent of the JS I wrote for the site:

this.setSelectionRange(0, this.value.length);

Aside: I’m certainly not against large web apps—on the contrary, I love them. But for this particular project I chose to revel in the minimalism of leveraging CSS to handle almost all of my site interactions.

Migrating from Jekyll to Hexo

TL;DR: I migrated my blog from Jekyll to Hexo. This is everything you need to know to do the same.

Initializing Hexo in your existing Jekyll project

Here’s what I did:

Back up your existing Jekyll blog to a subdirectory so you can wildly make changes with impunity:

mkdir jekyll.bak

Now move all the non-git files in the directory into jekyll.bak. Initially I just did it in finder, but then I got curious about how to do it in bash. So if you want to just run a nifty command here you go:

find . -maxdepth 1 -mindepth 1 ! -name '.git' ! -name 'jekyll.bak' -exec mv '{}' ./jekyll.bak/ \;
Read More

Getting Started with Flow and Webpack

TL;DR: This post will show you how to get set up with Flow and Webpack as quickly as possible so that you can benefit from some degree of type safety in your JS code!

What are we talking about?

Flow

Flow is like ESLint on steroids. It is a static type checker for JavaScript. It let’s you add types to any existing JS code. Why would you want to do this? The short answer is because JS ❤️ runtime errors, but runtime errors make your users sad. The more helpful answer is that Flow will analyze your codebase and catch bugs that may otherwise go unnoticed until you’re app is already deployed to production. There is an entire site dedicated to explaining Flow, so I will let you check that out for more detailed information: http://flowtype.org/

Read More

Testing Webpack Plugins

TL;DR: I’m going to show you how to test Webpack plugins. I’ll even show you how to integrate with a CI server 😄. The trick is to use the Webpack Node API.

Ever built a Webpack Plugin? Ever wondered how to test that awesome plugin you just built? If so, this article is for you.

I scoured the internet (skimmed the first page of a google search) for resources on testing Webpack plugins and came up empty, so I decided it was time to take matters into my own hands!

I recently built my own Webpack plugin for generating static sites from React Router routes. You can check it out here if interested. Anyway, I was getting somewhat annoyed that I hadn’t yet tested the plugin. I had seen some regressions as I added support for more features and it was no fun to have to resolve those issues when I really just anted to generate awesome static sites using React and Webpack.

Where to turn…

Read More

The Importance of Community

Specifically, when making technical decisions.

Community his hugely important any many areas of life, but lately I’ve found that it’s particularly pertinent in making technical decisions.

An example

Recently at Trustar we started using a graph database. We have a lot of interrelated data so the graph model has made conceptual sense for a long time. So this year when we decided to implement a graph database in production we had a technical decision to make: Which database do we use?

We’re a small team (currently ~5 engineers) so we definitely don’t have the bandwidth to build our own implementation. That means we need to choose among the existing solutions. There are currently several graph database providers in the wild, and they all seem to do pretty much the same thing. Technically speaking, the usual points they hit in differentiating themselves are ones of how they handle scalability and replication.

Read More

Teaching a React.js Workshop

Who wants to be a speaker? This guy! 🙌

Ian Speaking at Real World React Meetup

Last Sunday was a significant event for me: I put on a React workshop (link here) to teach beginners how to get started with React. It was a full day event and it was a great learning experience for myself and hopefully for the students as well.

Giving back to the community

The community surrounding React has done a lot for me. It’s only been a year since I started using this technology, but it’s played a tremendous role in my personal development as a programmer. As such, I really wanted to start contributing back to this amazing community that has helped nurture my own development.

Read More