Create Gatsby responsive blog website with markdown files
In this article i will explain you step by step to create gatsby blog website using markdown files
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;