Create Gatsby responsive blog website with markdown files

Author
April 06, 2022

In this article i will explain you step by step to create gatsby blog website using markdown files

blog-listing

Step 1: Install gatsby cli to use gatsby command globally

npm install -g gatsby-cli

Step 2: Create new project using gatsby

gatsby new personal-blog

Step 3: Install bootstrap, jquery and call it in gastby-browser.js file

In order to use bootstrap and jquery in our gatsby blog website we need to install bootstrap and jquery and call the installed package inside gatsby-browser.js file

npm install bootstrap jquery @popperjs/core --save

Update gatsby-browser.js with the following code

//Update gatsby-browser.js file with the following
//code after installing above package

/**
 * Implement Gatsby's Browser APIs in this file.
 *
 * See: https://www.gatsbyjs.com/docs/browser-apis/
 */

// You can delete this file if you're not using it
import "bootstrap/dist/css/bootstrap.min.css";
import "jquery/dist/jquery.min.js";
import "@popperjs/core/dist/umd/popper.min.js";
import "bootstrap/dist/js/bootstrap.min.js";

Step 4: Adding plugins to the project we created above

npm install --save gatsby-plugin-catch-links gatsby-plugin-react-helmet gatsby-source-filesystem gatsby-transformer-remark

gatsby-plugin-catch-links implements the history pushState API and does not require a page reload on navigating to a different page in the blog

gatsby-plugin-react-helmet allows for modification of the head tags

gatsby-source-filesystem is a gatsby source plugin for sourcing data into your Gatsby application from your local filesystem like markdown files

gatsby-transformer-remark a transformer plugin takes some underlying data format that is not inherently usable in its current form (e.g. Markdown, json, yaml, etc.) and transforms it into a format that Gatsby can understand and that you can query against with GraphQL

Step 5: Update gatsby-config.js file with all the plugins installed above

Create blog folder under src folder in your project where you will be creating markdown files for your blog and update the path under gatsby-source-filesystem plugin as done in the code below

//Update gatsby-config.js with the following code
module.exports = {
  siteMetadata: {
    title: `Personal Blog`,
    description: `Personal blog with markdown files`,
    author: `@gatsbyjs`,
    siteUrl: `http://localhost:8000`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-image`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `${__dirname}/src/blog`,
        name: `blog`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: "gatsby-transformer-remark",
      options: {
        plugins: [],
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
  ],
};

Step 6: Creating our first blog using markdown files

//Update src/blog/myfirst-blog/index.md with the following code
// if there is no such file and folder create it
---
path: "/myfirst-blog"
date: 2022-5-20T17:12:33.962Z
title: "My First Gatsby blog Post"
---
Hello, my first blog post!

Step 7: Create Template for your blog post

In order to render your blog on path /myfirst-blog and display the correct content we need to create a template name blog-post.js under src/templates folder for our blog post and display the content. Every time path changes content will change

//Update src/templates/blog-post.js with the following code
//Create the file and folder if it doesn't exist

import * as React from "react";
import Layout from "../components/layout";
import Seo from "../components/seo";
import { graphql } from "gatsby";

const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data;
  return (
    <Layout>
      <Seo
        description={post.frontmatter.description}
        title={post.frontmatter.title}
      />
      <div className="container">
        <div className="row">
          <div style={{ minHeight: "100vh" }} className="col-md-8 mx-auto mt-5">
            <h1>{post.frontmatter.title}</h1>
            <div
              className="blog-post-content"
              dangerouslySetInnerHTML={{ __html: post.html }}
            />
          </div>
        </div>
      </div>
    </Layout>
  );
};

export const pageQuery = graphql`
  query BlogPostByPath($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        title
        description
      }
    }
  }
`;

export default BlogPost;

Step 8: Create Static pages for each blog

Here for blog we don’t have calculated estimated that how many blogs we will be creating may be we can create 10 blogs or 50 or may be 100 and more so accordingly we need to create pages dynamically as per mark down files thus we will be writing code for creating such pages for each blog which we have created using markdown files and we will be using same template for every blog

Update the following code in gatsby-node.js file to create pages for our blog using template as blog-post.js

//Update gatsby-node.js file with the following code

const path = require("path");

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions;

  const blogPostTemplate = path.resolve(`src/templates/blog-post.js`);

  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              path
            }
          }
        }
      }
    }
  `);

  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`);
    return;
  }

  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.frontmatter.path,
      component: blogPostTemplate,
      context: {}, // additional data can be passed via context
    });
  });
};

Once updated run below command to start the project

npm run develop

Visit this url http://localhost:8000/myfirst-blog you will be able to see the blog listing

Step 9: Create blog listing page

Update your src/pages/index.js page with the following code

//Update pages/index.js page or any other page of your choice
// for blog listing

import * as React from "react";
import Layout from "../components/layout";
import Seo from "../components/seo";
import { Link, graphql } from "gatsby";
//import * as styles from "../components/index.module.css"

const IndexPage = ({ data }) => {
  const { edges: posts } = data.allMarkdownRemark;
  return (
    <Layout>
      <Seo title="Home" />

      <main role="main">
        <section className="jumbotron text-center">
          <div className="container">
            <h1 className="jumbotron-heading">My Blog</h1>
            <p className="lead text-muted">
              Something short and leading about the collection below—its
              contents, the creator, etc. Make it short and sweet, but not too
              short so folks don't simply skip over it entirely.
            </p>
          </div>
        </section>
        <div className="album py-5 bg-light">
          <div className="container">
            <div className="row">
              {posts
                .filter((post) => post.node.frontmatter.title.length > 0)
                .map(({ node: post }) => {
                  return (
                    <div className="col-md-4">
                      <div className="card mb-4 box-shadow">
                        <img
                          className="card-img-top"
                          data-src="holder.js/100px225?theme=thumb&bg=55595c&fg=eceeef&text=Thumbnail"
                          alt="Thumbnail [100%x225]"
                          style={{
                            height: "225px",
                            width: "100%",
                            display: "block",
                          }}
                          src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22348%22%20height%3D%22225%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20348%20225%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_180ec0d40cf%20text%20%7B%20fill%3A%23eceeef%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A17pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_180ec0d40cf%22%3E%3Crect%20width%3D%22348%22%20height%3D%22225%22%20fill%3D%22%2355595c%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22116.7109375%22%20y%3D%22120.15%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E"
                          data-holder-rendered="true"
                        />
                        <div className="card-body">
                          <h5>
                            <Link
                              style={{ color: "#000" }}
                              to={post.frontmatter.path}
                            >
                              {post.frontmatter.title}
                            </Link>
                          </h5>
                          <p className="card-text">{post.excerpt}</p>
                        </div>
                      </div>
                    </div>
                  );
                })}
            </div>
          </div>
        </div>
      </main>
    </Layout>
  );
};

export default IndexPage;

export const pageQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(sort: { order: DESC, fields: [frontmatter___date] }) {
      edges {
        node {
          excerpt(pruneLength: 250)
          id
          frontmatter {
            title
            date(formatString: "MMMM DD, YYYY")
            path
          }
        }
      }
    }
  }
`;

Step 10: Update header.js, footer.js ,layout.css to beautify your blog with bootstrap css

Update /src/components/layout.css with the following css

/*Update /src/components/layout.css with the following css*/

:root {
  --jumbotron-padding-y: 3rem;
}

.jumbotron {
  padding-top: var(--jumbotron-padding-y);
  padding-bottom: var(--jumbotron-padding-y);
  margin-bottom: 0;
  background-color: #fff;
}
@media (min-width: 768px) {
  .jumbotron {
    padding-top: calc(var(--jumbotron-padding-y) * 2);
    padding-bottom: calc(var(--jumbotron-padding-y) * 2);
  }
}

.jumbotron p:last-child {
  margin-bottom: 0;
}

.jumbotron-heading {
  font-weight: 300;
}

.jumbotron .container {
  max-width: 40rem;
}

footer {
  padding-top: 3rem;
  padding-bottom: 3rem;
}

footer p {
  margin-bottom: 0.25rem;
}

.box-shadow {
  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.05);
}

Update src/components/header.js file with the following code

//Update src/components/header.js file with the following code

import * as React from "react";
import PropTypes from "prop-types";
import { Link } from "gatsby";

const Header = ({ siteTitle }) => (
  <header>
    <div className="collapse bg-dark" id="navbarHeader">
      <div className="container">
        <div className="row">
          <div className="col-sm-8 col-md-7 py-4">
            <h4 className="text-white">About</h4>
            <p className="text-muted">
              Add some information about the album below, the author, or any
              other background context. Make it a few sentences long so folks
              can pick up some informative tidbits. Then, link them off to some
              social networking sites or contact information.
            </p>
          </div>
          <div className="col-sm-4 offset-md-1 py-4">
            <h4 className="text-white">Contact</h4>
            <ul className="list-unstyled">
              <li>
                <Link to="/" className="text-white">
                  Follow on Twitter
                </Link>
              </li>
              <li>
                <Link to="/" className="text-white">
                  Like on Facebook
                </Link>
              </li>
              <li>
                <Link to="/" className="text-white">
                  Email me
                </Link>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
    <div className="navbar navbar-dark bg-dark box-shadow">
      <div className="container d-flex justify-content-between">
        <Link to="/" className="navbar-brand d-flex align-items-center">
          <strong>{siteTitle}</strong>
        </Link>
        <button
          className="navbar-toggler"
          type="button"
          data-toggle="collapse"
          data-target="#navbarHeader"
          aria-controls="navbarHeader"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span className="navbar-toggler-icon" />
        </button>
      </div>
    </div>
  </header>
);

Header.propTypes = {
  siteTitle: PropTypes.string,
};

Header.defaultProps = {
  siteTitle: ``,
};

export default Header;

Update src/components/footer.js file with the following code

//Update src/components/footer.js with the following code
//If file doesn't exist create the file under same path

import * as React from "react";
import PropTypes from "prop-types";
//import { Link } from "gatsby"

const Footer = ({ siteTitle }) => (
  <footer className="text-muted">
    <div className="container">
      <p>
        {" "}
        © {new Date().getFullYear()} {siteTitle}
      </p>
    </div>
  </footer>
);

Footer.propTypes = {
  siteTitle: PropTypes.string,
};

Footer.defaultProps = {
  siteTitle: ``,
};

export default Footer;

Update src/components/layout.js file with the following code

/**
 * Layout component that queries for data
 * with Gatsby's useStaticQuery component
 *
 * See: https://www.gatsbyjs.com/docs/use-static-query/
 */

import * as React from "react";
import PropTypes from "prop-types";
import { useStaticQuery, graphql } from "gatsby";

import Header from "./header";
import Footer from "./footer";
import "./layout.css";

const Layout = ({ children }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `);

  return (
    <>
      <Header siteTitle={data.site.siteMetadata?.title || `Title`} />
      {children}
      <Footer siteTitle={data.site.siteMetadata?.title || `Title`} />
    </>
  );
};

Layout.propTypes = {
  children: PropTypes.node.isRequired,
};

export default Layout;
Congratulations! you have just created a blog website