How To Build a Simple Blog Using Gatsby and Contentrain

Contentrain Headless CMS
10 min readAug 11, 2022

--

Build a Simple Blog using Gatsby and Contentrain

Before Contentrain was created, we thought of giving developers the ability, flexibility, and all the goodies that Github entails. Most developers only believe that Git stores only source code but with time, technology has advanced, and so did Git. Today Git is one of the most powerful tools that is used by every tech company today, and most require you have a good knowledge of Git before you are even hired in the company.

Contentrain is built on git to help developers on the go as they are already very familiar with git. The option of not having to pay for storage space or choose where you wish to save your data is very much one of the most remarkable opportunities that git-based cms like Contentrain offers.

Developers have the ability to build blazing-fast websites using their favorite framework. In this tutorial, we shall be building a simple blog that has a list of blogs and on click on one gives us full detail of the blog post. using Gatsby and Contentrain The image below shows what our gatsby app should look like

Setting up our Gatsby project

In this section, we will see how to set up our project using one of Gatsby’s starters. To use the starters, we first fork the project.

To use the template, we have to run the command below:

npx gatsby new contentrain-blog https://github.com/maxiggle/newblog-starter.git

Once we have our project in our local machine, we can now set up our headless cms

Setting up Contentrain

In this section, we will see how we can setup Contentrain for our blog project using the stated steps below:

  • First, create an account with us here or if you already have an account, you can sign in.
  • Once your account is created, on the top right part of your screen, there is a start a new project button, click the button so you can start creating a project.
  • Once the button is pressed, a modal will show on our screen to name our project and to select a starter but we will choose manual as we will be using a gatsby template to help us build our blog even faster.

After providing the necessary details, we can hit create which will authenticate with GitHub which will ask us to choose the GitHub repository we wish to use for our project.

If you cannot find the project you created, you can click on the Github settings button at the top right of your screen which will prompt us to install Contentrain in that repository like below:

Scroll to the bottom of the screen to choose the repo you want to us

After choosing which repository you want to use, we can save the changes by clicking on the save changes button right after choosing our repository. Once we do this, we can now select this project on Contentrain

Once that is done, a pop-up will prompt us to choose a working branch for Contentrain or create a new one while we will leave others as default.

Once submitted, we can create roles for different users if there are a lot of collaborators but in our case, I will be using the default settings.

Contentrain provides quick starter collection templates which we will be using in my case or you can create your own custom collection.

After creating our collection, we click next and our project is set up and ready to use.

To start creating content to display on our front end, we can navigate to the Content and select Posts where you can add content in markdown.

File Structure

├─ Posts │ ├─ secondpost.md │ └─ test.md ├─ contentrain │ └─ Posts │ └─ Posts.json ├─ src │ ├─ components │ │ ├─ bio.js │ │ ├─ layout.js │ │ └─ seo.js │ ├─ images │ │ ├─ gatsby-icon.png │ │ └─ profile-pic.png │ ├─ pages │ │ ├─ 404.js │ │ └─ index.js │ ├─ templates │ │ └─ blog-post.js │ ├─ normalize.css │ └─ style.css └─ gatsby-config.js

Building Our Frontend

Recall that we created a new gatsby project and cloned it on our local machine. Open that project on your favorite code editor, mine is vs code, and let’s write some gatsby.

Bio Components

import * as React from "react" import { useStaticQuery, graphql } from "gatsby" const Bio = () => { const data = useStaticQuery(graphql` query BioQuery { site { siteMetadata { author { name summary } } } } `) // Set these values by editing "siteMetadata" in gatsby-config.js const author = data.site.siteMetadata?.author return ( <div> {author?.name && ( <p> <h2>All Blog Posts</h2> </p> )} </div> ) } export default Bio

The code above is contained in the component folder component/bio.js. We want details about ourselves to show when we publish articles on our personal blog. We can query those details from our siteMetadata in our config file gatsby-config.js. SiteMetadata is very important because it helps with SEO. That is, it has information that helps Google's search engine understand when to display our content if queried by a client, which is usually served by a CDN. If we also want to use small pieces of data around our application, we can add that data to the siteMetadata.

siteMetadata: { title: `Gatsby Starter for Contentrain`, author: { name: `Godwin Alexander Ekainu`, summary: `who lives and work Nigeria building useful things.`, }, description: `A starter blog demonstrating what Gatsby can do.`, siteUrl: `https://gatsbystarterblogsource.gatsbyjs.io/`, },

To access this data, we will use GraphQL and useStaticQuery Hook to pull the data to our frontend, you can read more here.

Layout component

import * as React from "react" import { Link } from "gatsby" const Layout = ({ location, title, children }) => { const rootPath = `${__PATH_PREFIX__}/` const isRootPath = location.pathname === rootPath let header if (isRootPath) { header = ( <h1 className="main-heading"> <Link to="/">{title}</Link> </h1> ) } else { header = ( <Link className="header-link-home" to="/"> {title} </Link> ) } return ( <div className="global-wrapper" data-is-root-path={isRootPath}> <header className="global-header">{header}</header> <main>{children}</main> <div > <footer> © {new Date().getFullYear()}, Built with {` `} <a href="https://www.gatsbyjs.com">Gatsby </a> {` and `} <a href="https://contentrain.io/">Contentrain</a> </footer> </div> </div> ) } export default Layout

You can copy the code above into your layout.js file. The code above displays the main header when on the default home page and when we click on a blog post to read. When a blog post is selected, the new header becomes a link, when clicked on, navigate back to our index page which is made possible by the Gatsby Link API. Read more here

SEO Components

import * as React from "react" import PropTypes from "prop-types" import { Helmet } from "react-helmet" import { useStaticQuery, graphql } from "gatsby" const Seo = ({ description, lang, meta, title }) => { const { site } = useStaticQuery( graphql` query { site { siteMetadata { title description } } } ` ) const metaDescription = description || site.siteMetadata.description const defaultTitle = site.siteMetadata?.title return ( <Helmet htmlAttributes={{ lang, }} title={title} titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null} meta={[ { name: `description`, content: metaDescription, }, { property: `og:title`, content: title, }, { property: `og:description`, content: metaDescription, }, { property: `og:type`, content: `website`, }, ].concat(meta)} /> ) } Seo.defaultProps = { lang: `en`, meta: [], description: ``, } Seo.propTypes = { description: PropTypes.string, lang: PropTypes.string, meta: PropTypes.arrayOf(PropTypes.object), title: PropTypes.string.isRequired, } export default Seo

Every website needs SEO and to have it work on our application, we will use useStaticQuery and GraphQL to pull the necessary data from gatsby.config.js for our SEO component. You can read the official documentation here

Our Post

This is where we fetch our posts created on Contentrain which is committed by the cms into our GitHub project as an MD file, which when merged, we can pull into our local project.

import * as React from "react" import { Link, graphql } from "gatsby" import Bio from "../components/bio" import Layout from "../components/layout" import Seo from "../components/seo" const BlogPostTemplate = ({ data, location }) => { const post = data.markdownRemark const siteTitle = data.site.siteMetadata?.title || `Title` const { previous, next } = data return ( <Layout location={location} title={siteTitle}> <Seo title={post.frontmatter.title} description={post.frontmatter.description || post.excerpt} /> <article className="blog-post" itemScope itemType="http://schema.org/Article" > <header> <h1 itemProp="headline">{post.frontmatter.title}</h1> <p>{post.frontmatter.date}</p> </header> <section dangerouslySetInnerHTML={{ __html: post.html }} itemProp="articleBody" /> <hr /> <footer> <Bio /> </footer> </article> <nav className="blog-post-nav"> <ul style={{ display: `flex`, flexWrap: `wrap`, justifyContent: `space-between`, listStyle: `none`, padding: 0, }} > <li> {previous && ( <Link to={previous.fields.slug} rel="prev"> ← {previous.frontmatter.title} </Link> )} </li> <li> {next && ( <Link to={next.fields.slug} rel="next"> {next.frontmatter.title} → </Link> )} </li> </ul> </nav> </Layout> ) } export default BlogPostTemplate export const pageQuery = graphql` query BlogPostBySlug( $id: String! $previousPostId: String $nextPostId: String ) { site { siteMetadata { title } } markdownRemark(id: { eq: $id }) { id excerpt(pruneLength: 160) html frontmatter { title description } } previous: markdownRemark(id: { eq: $previousPostId }) { fields { slug } frontmatter { title } } next: markdownRemark(id: { eq: $nextPostId }) { fields { slug } frontmatter { title } } } `

For us to be able to convert the MD files to HTML pages, we can use the gatsby source file system to read the MD files from their folder.

export const pageQuery = graphql` query BlogPostBySlug( $id: String! $previousPostId: String $nextPostId: String ) { site { siteMetadata { title } } markdownRemark(id: { eq: $id }) { id excerpt(pruneLength: 160) html frontmatter { title description } } previous: markdownRemark(id: { eq: $previousPostId }) { fields { slug } frontmatter { title } } next: markdownRemark(id: { eq: $nextPostId }) { fields { slug } frontmatter { title } } } `

Based on the code above, we are using the gatsby transformer remark plugin to serialize markdown files which reads the metadata part as frontmatter and the contents as markdown, and using Graphql to query the results. This result is automatically passed to the data prop of our component so we can use it anywhere around our component.

const BlogPostTemplate = ({ data, location }) => { const post = data.markdownRemark const siteTitle = data.site.siteMetadata?.title || `Title` const { previous, next } = data

Our Home page

In this section, we will display the title of our post and its description and with the click of the title, we can view the content. To achieve this, use the code below in your index.js file.

import * as React from "react" import { Link, graphql } from "gatsby" import Bio from "../components/bio" import Layout from "../components/layout" import Seo from "../components/seo" const BlogIndex = ({ data, location }) => { const siteTitle = data.site.siteMetadata?.title || `Title` const posts = data.allMarkdownRemark.nodes if (posts.length === 0) { return ( <Layout location={location} title={siteTitle}> <Seo title="All posts" /> <Bio /> <p> No blog posts found. Add markdown posts to "content/blog" (or the directory you specified for the "gatsby-source-filesystem" plugin in gatsby-config.js). </p> </Layout> ) } return ( <Layout location={location} title={siteTitle}> <Seo title="All posts" /> <Bio /> <ol style={{ listStyle: `none` }}> {posts.map(post => { const title = post.frontmatter.title || post.fields.slug return ( <li key={post.fields.slug}> <article className="post-list-item" itemScope itemType="http://schema.org/Article" > <header> <h2> <Link to={post.fields.slug} itemProp="url"> <span itemProp="headline">{title}</span> </Link> </h2> <small> {new Date(post.frontmatter.createdAt).toLocaleString()} </small> </header> <section> <p dangerouslySetInnerHTML={{ __html: post.frontmatter.description || post.excerpt, }} itemProp="description" /> </section> </article> </li> ) })} </ol> </Layout> ) } export default BlogIndex export const pageQuery = graphql` query { site { siteMetadata { title } } allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { nodes { excerpt fields { slug } frontmatter { createdAt title description } } } } `

Based on the code above, we use the transform remark plugin to serialize our markdown files which when queried by the Graphql, the result is returned as a list of posts that we will display on our homepage.

if (posts.length === 0) { return ( <Layout location={location} title={siteTitle}> <Seo title="All posts" /> <Bio /> <p> No blog posts found. Add markdown posts to "content/blog" (or the directory you specified for the "gatsby-source-filesystem" plugin in gatsby-config.js). </p> </Layout> ) }

Here we make a check if there is no post, it should return a default text else render the post to our home page.

Conclusion

In this tutorial, we have learned how to easily build a blog using Gatsby and populate our gatsby app using Contentrain. We have also seen how easy it is to work with git-based cms like Contentrain. To take it a bit further, you can clone the project and build it to your test and add more content.

Resources

Originally published at https://contentrain.io.

--

--