May 15, 2018
Update June 2019: This Tutorial was written for Gatsby V1. In Gatsby V2 the layouts behavior described in this post changed. You can get the old behaviour by loading the layout plugin.
Recently I ported my website to GatsbyJs. In my previous setup I already wrote my blog posts in markdown, but had a kind of self-built way of displaying those markdown files, which I wasn’t perfectly happy with. For those who don’t know GatsbyJs: It’s a static site generator, that is built on top of React and has a ton of great plugins, that are very easy to setup and use.
So what’s the difference to the regular Create-React-App (CRA)? There is a great GitHub issue explaining the difference: Mainly that Gatsby is built on top of CRA, runs on GraphQL out of the box and has more build opinions. Further once you build your site with Gatsby, you’ll get a set of static sites rendered on the server instead of a dynamic application rendered on the client (you can also add server side rendering to CRA, but it’s not out of the box). CRA on the other hand is more open in how you handle data and is just a quick and easy way to get started with React without having too many pre-defined ways of how your application should be built.
In order to understand, which part of my old application I should port into the new Gatsby setting, I first read the Quick-Start guide and installed the Gatsby CLI npm install --global gatsby-cli
. After exploring the basic starter you get when running gatsby new gatsby-site
, I mainly looked through the code of various of the starters to figure out which parts I needed an didn’t need and what I had to change in order to get the same experience I had now with the old redux setting. I decided to not use Redux anymore, since my portfolio website doesn’t have a lot of state and I really enjoy the simple Gatsby way of using the remark plugin and writing page specific state into the markdown files and then querying it with GraphQL.
When looking through various starters, I always found myself back in two main files: gatsby-config.js
and gatsby-node.js
, because they quickly tell you what’s happening in the project and what's built in. I decided to split my static pages markdown files and my blog post markdown files in two different folders called pages
and posts
. Those then will be rendered into the templates located into the templates
folder. Markup that's valid for all pages goes into the layout/index.js
file.
The config file explains what plugins you’re using with what options. You can find my complete config here: Gatsby Config
The gatsby-source-filesystem
plugin is essential to get your markdown files loaded. I created to seperate folders for my static pages and for my blog posts, with the name option name: "pages”
you’re later able to query your specific pages or posts files in GraphQL.
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/pages/`,
name: "pages",
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/posts/`,
name: `posts`,
},
},
The node file is where the magic happens. It’s where your markdown files and templates are loaded and where your different paths are created. In order for my site to work I needed to load different templates depending if the markdown file was in the posts
or pages
folder.
const pageTemplate = path.resolve('./src/templates/static-page.js')
const postTemplate = path.resolve("./src/templates/blog-post.js");
I also adapted the GraphQL remark query to filter for only posts or pages:
allMarkdownRemark(filter: { id: { regex: "//posts|pages//" } }, limit: 1000) { ... }
and then further down, when my pages were created I loaded the correct template depending on in which folder the markdown files was in.
const posts = result.data.allMarkdownRemark.edges;
_.each(posts, (post, index) => {
const slug = post.node.fields.slug;
const isPost = /posts/.test(post.node.id);
createPage({
path: slug,
component: isPost ? postTemplate : pageTemplate,
context: {
slug: slug,
},
})
})
Markdown is awesome and simplifies writing on the web a lot. If you haven't used it yet, you can get started with this nice markdown tutorial. GatsbyJS makes it really easy to transform your markdown content into HTML structure using the remark plugin. In order to make it work for your project there is a couple of things you need to consider.
You can have different locations for your markdown files, just specify where they are located in the gatsby-config.js
for the gatsby-source-filesystem
plugin.
Once you have your files setup, you can query your markdown files in GraphQL using either allMarkdownRemark
for getting multiple markdown files or simply markdownRemark
for getting a simple markdown file.
To get a specific markdown file you could do the following:
query IndexQuery {
markdownRemark(frontmatter: {title: {eq: "about"}}) {
html
frontmatter {
title
color
}
}
}
This would go through your markdown files and get the one, that has title: about
set.
Or to get all markdown files located within your posts folder, you could do the following:
query BlogQuery {
allMarkdownRemark(
filter: {fileAbsolutePath: {regex: "/(posts)/.*\\.md$/"}},
sort: { fields: [frontmatter___date], order: DESC },
limit: 1000) {
edges {
node {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "DD MMMM, YYYY")
title
tags
}
}
}
}
}
For my website the main animation happens whenever we go to a new page. Since the component, that creates this animation is on all my pages, it is located in my layouts/index.js
file. In order for the component to receive this location change the only thing I had to add was a location prop, so I could add the animation on componentDidUpdate
.
class Template extends React.Component {
render() {
const { location, children } = this.props
return (
<div>
<Frame className="frame" location={location} />
...
</div>
)
}
}
So in my Frame Component, whenever the location updated, my component updated and I added a simple CSS Class, which I removed again, after the animation had finished.
export class Frame extends React.PureComponent {
componentDidUpdate() {
this.overlay.classList.add('animating');
setTimeout(() => { this.overlay.classList.remove('animating'); }, 1200);
}
render() {
...
}
}
The animation itself is defined with styled components and CSS Keyframes.
const scaleUpDown = keyframes`
0% { transform: scaleY(0) }
40% { transform: scaleY(1); animation-timing-function: cubic-bezier(0.075, 0.820, 0.165, 1.000); }
80% { transform: scaleY(1); animation-timing-function: cubic-bezier(0.600, 0.040, 0.980, 0.335); }
100% { transform: scaleY(0) }
`;
and my Overlay had an animation class, which triggered the animation whenever the .animating
class was added.
const Overlay = styled.span`
...
&.animating {
animation: ${scaleUpDown} 1s 0s both cubic-bezier(0.445, 0.050, 0.550, 0.950);
}
`;
I haven’t added fadeOut transitions myself but there is a very handy gatsby plugin called gatsby-plugin-page-load-delay that delays the unmount of your component, which allows you to add animation on the componentWillUnmount
lifecycle hook. By setting the window.pageExitTime = 1000
to the time of your animation, the next page will be mounted as soon as the animation is finished.
If you have animations that aren’t specific to your entire layout, but only to one page, you can trigger these animations in the componentDidMount
hook of the specific page. For my website this is the case with the different main color on the pages. My main color is defined as a CSS Variable on my document.body and whenever we’re going to a new page this is updated. Which color the page should have is defined in the markdown file of the page itself:
---
title: Speaking
color: '#4AD9D9'
---
Some markdown text
And in my PageTemplate I’m setting the --main
CSS Variable on my document.body
, which updates the color for the body and all the children that use this variable.
class PageTemplate extends React.Component {
componentDidMount() {
const color = get(this, 'props.data.markdownRemark.frontmatter.color') || '#F5A503';
document.body.style.setProperty('--main', color);
}
render() { ... }
}
NOTE: In order to add new fields to the markdown files like I added the color field, you need to load the gatsby-transformer-javascript-frontmatter plugin and query the field in the GraphQL query of the template.
markdownRemark(fields: { slug: { eq: $slug } }) {
id
html
frontmatter {
title
color
}
}
The Overlay that is animated when we go to another page for example, has the background and a transition defined dependent on the --main
CSS variable.
background: var(--main);
transition: background 0.3s ease-out;
Something I find awesome and amazing is the ability to create custom components, that you can use within your markdown files like I did on my /work
page:
---
title: Work
color: '#F2385A'
---
<work-project title="Illustration" subtitle="Drawings" link="https://www.instagram.com/lisi.line.art/"></work-project>
There is a great tutorial on how to create these components and for me this allowed to have one single PageTemplate
for all my static files instead of 5 different files likes index.js
, work.js
, writing.js
, speaking.js
. Then I just defined the page specific components within the markdown file.
Once you finished the development of your Gatsby site you have different options to deploy your site. When you run gatsby build
a public folder will be created within your project and you can just copy these static files via FTP to your server, but the best option for me is to add a continuous deployment, which rebuilds your site when you push changes to your GitHub repository.
If you already own a webspace and don’t want to switch to something else a tool like CircleCI is free, if you don’t have tons of builds. It's fairly easy to setup with an exisiting GitHub repository. Here is a simple circleCI setup you can use for your Gatsby site. In order to use it, all you need is a ssh access to your webspace and to add your credentials for the deploy jobs.
command: scp -r public "$SITE_SSH_USER@$SITE_SSH_HOST:/var/www/$PROD_URL/"
If you don’t have a webspace the easiest option is to use Netlify, because it integrates very nicely with GatsbyJS and you can even hook it up to NetlifyCMS, allowing you to have a CMS Editor for your static site. Steps for using Netlify to deploy your project:
The process of creating a new site is very simple and straight forward in Netlify. Just connect your Github Repo and get started.
Once you deployed your project you can point your domain to the netlify deployment, so you have a nice domain name for your project.
After everything is ready, you can edit your Continuous Deployment Settings to tell Netlify when it should rebuild your project. The basic settings are that your site is rebuilt whenever you push on master on your GitHub repo
In my opinion GatsbyJS is great in the following cases: