Skip to main content

Gatsby Blog Starter

Harbin, China

For an overview of the project structure please refer to the Gatsby documentation - Building with Components

Install this starter (assuming Gatsby is installed) by running from your CLI:

gatsby new gatsby-blog

This is the second mini-project that I am going to use Gatsby in - check out the first one for a more detailed description of all the basics steps of setting up Gatsby.

01 gatsby-source-filesystem

First we install a plugin that allows us to pull in data. There are a couple of Source Plugins available to get your data from MongoDB or a CMS source like Wordpress. But we only want to grab Markdown files from our local file system -> hence we are going to install gatsby-source-filesystem, using npm:

npm install --save gatsby-source-filesystem

The plugin then has to be added to our Gatsby build by adding it to /gatsby-config.js. It is added as an object, carrying some configuration - in this case the path to the directory that will store our Markdown files:

plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
},
},
],

02 gatsby-transformer-remark

Since we want to use Markdown as a data source, we need a transformer plugin, that turns the Markdown into something that GraphQL can query against. This is the gatsby-transformer-remark plugin, that can also be installed by npm:

npm install --save gatsby-transformer-remark

This plugin comes without any additional configuration, and can simply be added to gatsby-config.js:

plugins: [
`gatsby-transformer-remark`,
]

Now we can start the Gatsby development environment with:

gatsby develop

The app will start on http://localhost:8000

Gatsby Blog Starter

03 Creating a first Blog Post

Now we want to prepare our first blog post. We can add a folder to the pages directory with the date of today ~ as this might make sense to a blog (naming convention is up to you) -> /src/pages/2017-10-05-first-post. Now add a new file to this folder, named index.md. The Markdown file needs some information at the top of the file - called FrontMatter:

---
path: '/first-post'
title: 'First Blog Post'
date: '2017-10-05'
---

This FrontMatter will be used to Query against with GraphQL and can carry a couple of different information - see also my previous repo. But we only use it to add an title to our blogpost and assign an URL under which we want to be able to access the file. Now just add some Markdown to the file and save it:

## Hello from Markdown!
---

### This is a first blog Post

How exciting Web Development can be, amazing!

The markdown represents the data that is going to be displayed. But now we need to create a style template that is used with this data. Lets start by adding a new folder inside /src called templates. Now add a file to it called post.js that will contain the structure template for every post entry. The file contains the JSX markup for our post:

import React from 'react';
import Helmet from 'react-helmet';

export default function Template({data}) {
const {markdownRemark: post} = data;
// const post = data.markdownRemark;
return (
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{__html: post.html}} />
</div>
)
}

The <Template /> component receives {data} props, that are retrieved by an GraphQL query:

export const postQuery = graphql`
query BlogPostByPath($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path} }) {
html
frontmatter {
path
title
}
}
}
`

The query looks for a markdown post, where the called URL equals the $path given inside it's frontmatter. So if the URL that you type into your browser was /first-post, a markdown file with a path: '/first-post' inside it's frontmatter, would be a hit.

The query then uses the markdownRemark plugin to transform the post markdown to HTML and make both the path and title from it's frontmatter available iside {data}, that is passed down into the <Template /> component and then rendered.

Gatsby is already configured to route all pages inside /src/pages as pages for our website. But now we have to register our posts, that are from the markdown files and the post.js template. To do this, we have to create a file named gatsby-node.js inside the root directory of our app. We are going to use the createPages Gatsby API to create pages from our post template:

const path = require('path');

exports.createPages = ({boundActionCreators, graphql}) => {
const {createPage} = boundActionCreators;
// const createPage = boundActionCreators.createPage;

const postTemplate = path.resolve('src/templates/post.js');

return graphql(`{
allMarkdownRemark {
edges {
node {
html
id
frontmatter {
path
title
}
}
}
}
}`)
.then(res => {
if(res.errors) {
return Promise.reject(res.errors);
}

res.data.allMarkdownRemark.edges.forEach(({node}) => {
createPage({
path: node.frontmatter.path,
component: postTemplate
})
})

})
}

Save and restart your app - then open http://localhost:8000/first-post inside your web browser:

Gatsby Blog Starter

04 Creating an Index Page

Now that we have a blog post (you can duplicate the first one a couple of times - changing the title, path and date), we will need an index page with a collection of links for all our posts. We will use the same GraphQL query used above to create this list.

We can add optional sorting, limiting parameters for the GraphQL query - see also 1, 2:

allMarkdownRemark(sort: { fields: [frontmatter___title], order: ASC})

allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC })

allMarkdownRemark(limit: 1000)

Gatsby comes with the GraphiQL debugger that allows us to test queries before we add them to our page - the debugger is running under: http://localhost:8000/___graphql

Gatsby Blog Starter

{
allMarkdownRemark(limit: 10, sort: {fields: [frontmatter___date], order: DESC}) {
edges {
node {
frontmatter {
path
title
date
}
}
}
}
}

This query shows the latest 10 Markdown posts in descending order - let's add it to our index page /src/pages/index.js. First we add the GraphQL query, below the <IndexPage /> component:

export const pageQuery = graphql`
query IndexQuery {
allMarkdownRemark(limit: 10
sort: {fields: [frontmatter___date], order: DESC}
) {
edges {
node {
id
frontmatter {
path
title
date
}
}
}
}
}
`

Then we inject the data from that query into the <IndexPage /> component and loop through it to generate our index:

const IndexPage = ({data}) => (
<div>
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<br/><br/>

<Link to='/page-2/'>Go to page 2</Link>
<br/><br/><br/><br/>

<h2>Index</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Link</th>
</tr>
</thead>
<tbody>
{data.allMarkdownRemark.edges.map(post => (
<tr key={post.node.id}>
<td>
{post.node.frontmatter.date}
</td>
<td>
<Link
to={post.node.frontmatter.path}>
{post.node.frontmatter.title}
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
)

Gatsby Blog Starter

05 Filters & Sorting With GraphQL

We already sorted our results by date and name in the examples above. Now let's add another value to our frontmatter, to decide whether a post is a draft or should already be shown as published. Set the first post to published: true and the second one to published: false :

---
path: '/second-post'
title: 'Second Blog Post'
date: '2017-10-07'
published: false
---

Now we just need to configure our GraphQL query to only show post where published is set to true:

export const pageQuery = graphql`
query IndexQuery {
allMarkdownRemark(
limit: 10
sort: {fields: [frontmatter___date], order: DESC}
filter: { frontmatter: { published: {eq: true} }}
) {
edges {
node {
id
frontmatter {
path
title
date
published
}
}
}
}
}
`

06 Static Serve

To serve our website we can use the command:

gatsby build

This will build a static version of our React app inside /public. Just upload it to a webserver or to GitHub Pages and you are good to go.

After building the static page, you can also use the command:

gatsby serve

That - unlike our development environment - fires up the webpack server in production mode - that means the files that are served, are the optimized files from the /public directory! You can access this version on http://localhost:9000