Building a blog site using gatsby.js, GraphQL for query Langauge and using local file system to get the blogs data.
Gatsby is an open-source framework that combines functionality from React, GraphQL and Webpack into a single tool for building static websites and apps.
GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.
React.js allows you to write JSX (Javascript XML) code which is similar to HTML syntax but not HTML, it’s just Javascript. It enables us to develop component-based applications and hence we can reuse the component anywhere in that project. These applications are SPA’s (Single Page Applications) and React internally uses Virtual DOM to make the rendering faster.
Front Matter is structured metadata that lives at the top of your content files that allows you to add custom variables to your pages. Depending on your static site generator.
Content files are files that are processed by your static site generator and output in a different format (usually
.html
). They are usually Markdown files (.md
,.markdown
) or HTML files (.html
).
Folder structure
Pages
Ok, so Let’s jump in and start together
- Setup and Installation
- make sure you have node js installed (for this proj we are using version — 14.15.0 ) Node.js website
- install gatsby CLI globally (npm install -g gatsby-cli)
- Now we will generate a new application using with command
- gatsby new name_of_the_app starter_url
- gatsby new blogsite (we are omiting starter template url for this project)
- Also we will try to deploy it to netlify too! (optional point)
- cd blogsite and open it using visual studio code or any other code editor
2. Folder structure
- open package.json file and we can see these packages installed VIZ,
gatsby-link so that we can use link tag like we would in regular react
gatsby-plugin-react-helmet and react-helmet to generate head info for your pages, which is used for SEO purpose
also as you can see it uses prettier extension for beautifying and formatting code
now in the scripts object we have gatsby develop to our dev server
gatsby build will build our optimized code and and convert it from ES6 or ES7 to ES5 version of JS which your browsers understand
We also have configuration files like
gatsby-config.js
gatsby-node.js (we will check them later when starting to implement our blog with markdown)
src folder will contain the application code
here we have Components, Pages and Layout folders
In the index.js file as you can see it’s a functional react component and we have an arrow function here
So the content inside this div is what being rendered and it is called JSX
- Add what is JSX here and explain it breifly
Link tag will be used to redirect to different pages
then we export page (so that the page is available outside for other components and pages)
3. React Helmet for SEO
- In index.js file only we have SEO component we are importing “react-helmet”
- from which we will destructure “Helmet”
- so, Helmet deals with the head section/tag and is used manage SEO
- As you can see we have title and also we can inlcude meta info such as description and keywords
- also we have GraphQL query below ( Optional point )
4. Styling ( CSS )
- so we can use global styling or SASS or styled components
- In this demo we are going to use css files and inline styles in some cases
5. In the Header.js (siteTitle)
- that we are passing it as a props and can be changed from “gatsby-config.js”
6. Run development server
- by running the command: gatsby develop
- it will open site on “localhost:8000”
- and this dev-server has hot reload (so as soon as you save your page browser gets updated with latest changes)
7. Explaining basic codes
- Link tag is used instead of anchor tag and it is obtained from “gatsby-link” package and has a “to” attribute
- we can change the “siteTitle” from gatsby-config.js . If we change config file we need to restart the dev server. (after reload show them head tag in the browser to view SEO tags)
8. Editing index.js
- add some dummy text and save and show them hot reload and tell them if they want they can use bootstrap, materliaze or CSS grid
import * as React from "react"import Layout from "../components/layout"
import Seo from "../components/seo"
import "./common.css";const IndexPage = () => (
<Layout>
<Seo title="Home" description="Blog site home page" />
<header id="page-title" className="jumbotron background">
<div className="container">
<div className="horizontal-center vertical-center">
<h1 className="article-title">Welcome to your own Gatsby site.</h1>
<h2 className="article-subtitle"><span>“</span>Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better.<span>”</span></h2>
</div>
</div>
</header>
</Layout>
)export default IndexPage
9. Creating a new page
- create a page inside of “Pages” folder called “about.js”
- Now to create a component we use VS-Code plugin called “ES7/React/Redux/GraphQL”
- then type ‘rfce’ and hit tab to create functional component
- rcc — will give you a class based component
- export that component and show the demo by going to about page.
- Generally in React we use react-router or Route but in Gatsby we don’t need to use that it handle that on it’s own (behind the scene)
- all you got to do is to create the page in the pages-folder and gatsby will handle it
import React from 'react'import Layout from "../components/layout"
import Seo from "../components/seo"
import "./common.css";const about = () => (
<Layout>
<Seo title="About" description="Blog site about page" />
<header id="page-title" className="jumbotron background">
<div className="container">
<div className="horizontal-center vertical-center">
<h1 className="article-title">About Us</h1>
<h2 className="article-subtitle"><span>“</span>When something is important enough, you do it even if the odds are not in your favor.<span>”</span></h2>
</div>
</div>
</header>
</Layout>
)export default about
- create a “services.js” page inside pages folder from about page and go to that route and demo that page
import React from 'react'import Layout from "../components/layout"
import Seo from "../components/seo"
import "./common.css";const services = () => (
<Layout>
<Seo title="Services" description="Blog site service page" />
<header id="page-title" className="jumbotron background">
<div className="container">
<div className="horizontal-center vertical-center">
<h1 className="article-title">Our Services</h1>
<h2 className="article-subtitle"><span>“</span>Don't watch the clock; do what it does. Keep going.<span>”</span></h2>
</div>
</div>
</header>
</Layout>
)export default services
10. Creating components
- Create “menu.js” file in components folder
- since we are using arrow fn we can git red of the curly braces and the return keyword too. (for all components and pages)
- Add inline style for the menu component and inside here everything is JS (like CSS will look like JS only [CSSinJS]) -here we are using flexbox and emmet to use emmet abbreviation (like: li*4 and press tab)
- add tag inside of li tag
- we will add blog menu later inside of the menu component
import { Link } from 'gatsby'
import React from 'react'const Menu = () => (
<div style={{
background: '#e5ebea',
paddingTop: '10px',
}}> <ul style={{
listStyle: 'none',
display: 'flex',
justifyContent: 'space-evenly',
marginBottom: 0,
}}>
<li>
<Link to="/" >Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/services">Services</Link>
</li>
<li>
<Link to="/blog">Blog</Link>
</li>
</ul> </div>
)export default Menu
- Then insert your the Menu in your layout or it will not show it on refresh
- Put the right below in the layout.js file
/**
* 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 Menu from "./menu"
import "./layout.css"const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
}
}
}
`) return (
<>
<Header siteTitle={data.site.siteMetadata?.title || `Title`} />
<Menu />
<div>
<main>{children}</main>
<footer
style={{
padding: `1rem`,
backgroundColor: '#e5ebea',
}}
>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.com">Gatsby</a>
</footer>
</div>
</>
)
}Layout.propTypes = {
children: PropTypes.node.isRequired,
}export default Layout
- Explain about the component based architecture of react in brief
11. Layout.css
- If you want to change common styles then you have to do it layouts.css file (ex: changing anchor tag styles etc)
html {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
font: 112.5%/1.45em georgia, serif, sans-serif;
box-sizing: border-box;
overflow-y: scroll;
}
body {
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: hsla(0, 0%, 0%, 0.8);
font-family: georgia, serif;
font-weight: normal;
word-wrap: break-word;
font-kerning: normal;
-moz-font-feature-settings: "kern", "liga", "clig", "calt";
-ms-font-feature-settings: "kern", "liga", "clig", "calt";
-webkit-font-feature-settings: "kern", "liga", "clig", "calt";
font-feature-settings: "kern", "liga", "clig", "calt";
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
main,
menu,
nav,
section,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
}
audio:not([controls]) {
display: none;
height: 0;
}
progress {
vertical-align: baseline;
}
[hidden],
template {
display: none;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
color: #333;
text-decoration: none;
}
a:active,
a:hover {
outline-width: 0;
}
abbr[title] {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
text-decoration: none;
}
b,
strong {
font-weight: inherit;
font-weight: bolder;
}
dfn {
font-style: italic;
}
h1 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 2.25rem;
line-height: 1.1;
}
mark {
background-color: #ff0;
color: #000;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
img {
border-style: none;
max-width: 100%;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
svg:not(:root) {
overflow: hidden;
}
code,
kbd,
pre,
samp {
font-family: monospace;
font-size: 1em;
}
figure {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
hr {
box-sizing: content-box;
overflow: visible;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: calc(1.45rem - 1px);
background: hsla(0, 0%, 0%, 0.2);
border: none;
height: 1px;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
margin: 0;
}
optgroup {
font-weight: 700;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
[type="reset"],
[type="submit"],
button,
html [type="button"] {
-webkit-appearance: button;
}button::-moz-focus-inner {
border-style: none;
padding: 0;
}button:-moz-focusring {
outline: 1px dotted ButtonText;
}
fieldset {
border: 1px solid silver;
padding: 0.35em 0.625em 0.75em;
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
textarea {
overflow: auto;
}
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
padding: 0;
}[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
* {
box-sizing: inherit;
}
*:before {
box-sizing: inherit;
}
*:after {
box-sizing: inherit;
}
h2 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1.62671rem;
line-height: 1.1;
}
h3 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1.38316rem;
line-height: 1.1;
}
h4 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 1rem;
line-height: 1.1;
}
h5 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 0.85028rem;
line-height: 1.1;
}
h6 {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
color: inherit;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
font-weight: bold;
text-rendering: optimizeLegibility;
font-size: 0.78405rem;
line-height: 1.1;
}
hgroup {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
ul {
margin-left: 1.45rem;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
list-style-position: outside;
list-style-image: none;
}
ol {
margin-left: 1.45rem;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
list-style-position: outside;
list-style-image: none;
}
dl {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
dd {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
p {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
pre {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 1.45rem;
font-size: 0.85rem;
line-height: 1.42;
background: hsla(0, 0%, 0%, 0.04);
border-radius: 3px;
overflow: auto;
word-wrap: normal;
padding: 1.45rem;
}
table {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
font-size: 1rem;
line-height: 1.45rem;
border-collapse: collapse;
width: 100%;
}
blockquote {
margin-left: 1.45rem;
margin-right: 1.45rem;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
form {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
noscript {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
iframe {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
address {
margin-left: 0;
margin-right: 0;
margin-top: 0;
padding-bottom: 0;
padding-left: 0;
padding-right: 0;
padding-top: 0;
margin-bottom: 1.45rem;
}
b {
font-weight: bold;
}
strong {
font-weight: bold;
}
dt {
font-weight: bold;
}
th {
font-weight: bold;
}
li {
margin-bottom: calc(1.45rem / 2);
}
ol li {
padding-left: 0;
}
ul li {
padding-left: 0;
}
li > ol {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
li > ul {
margin-left: 1.45rem;
margin-bottom: calc(1.45rem / 2);
margin-top: calc(1.45rem / 2);
}
blockquote *:last-child {
margin-bottom: 0;
}
li *:last-child {
margin-bottom: 0;
}
p *:last-child {
margin-bottom: 0;
}
li > p {
margin-bottom: calc(1.45rem / 2);
}
code {
font-size: 0.85rem;
line-height: 1.45rem;
}
kbd {
font-size: 0.85rem;
line-height: 1.45rem;
}
samp {
font-size: 0.85rem;
line-height: 1.45rem;
}
abbr {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
acronym {
border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
cursor: help;
}
thead {
text-align: left;
}
td,
th {
text-align: left;
border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
font-feature-settings: "tnum";
-moz-font-feature-settings: "tnum";
-ms-font-feature-settings: "tnum";
-webkit-font-feature-settings: "tnum";
padding-left: 0.96667rem;
padding-right: 0.96667rem;
padding-top: 0.725rem;
padding-bottom: calc(0.725rem - 1px);
}
th:first-child,
td:first-child {
padding-left: 0;
}
th:last-child,
td:last-child {
padding-right: 0;
}
tt,
code {
background-color: hsla(0, 0%, 0%, 0.04);
border-radius: 3px;
font-family: "SFMono-Regular", Consolas, "Roboto Mono", "Droid Sans Mono",
"Liberation Mono", Menlo, Courier, monospace;
padding: 0;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
pre code {
background: none;
line-height: 1.42;
}
code:before,
code:after,
tt:before,
tt:after {
letter-spacing: -0.2em;
content: " ";
}
pre code:before,
pre code:after,
pre tt:before,
pre tt:after {
content: "";
}
@media only screen and (max-width: 480px) {
html {
font-size: 100%;
}
}
12. How to implement a Blog (creating some blog posts)
- in the pages folder create a new folder called “2022–01–14-post-one”.
- inside of that folder create a new markdown file called “index.md”
---
path: "/post-one"
date: "2021-11-15"
title: "My first Gatsby post"
author: "Ameen Shaikh"
---This is my very **first** blog post in Gatsby
- In this markdown we will have posts which will be content
- we also have a frontmatter, which will go inbetween 2 triple hypes/dashes( — -)
ex:
---
{content}
---
- let’s create another blog post with front matter like before.
- create a new folder called “2021–11–15-post-two” and inside this create “index.md” file
---
path: "/post-two"
date: "2021-11-15"
title: "My Second Gatsby post"
author: "John Doe"
---This is my very second blog post in Gatsby
- Once everything is configured & you created client website, you can tell them to do create a blog post (DO NOT COVER)
13. Installing few plugins
- Now we need to create a way to access these pages
- npm i gatsby-source-filesystem
- This allows us to work with our local data (used to query our blogs from blog system)
- npm i gatsby-transformer-remark
- To transform our blog posts that are written in markdown (.md) files into HTML for rendering.
- npm i gatsby-plugin-catch-links
- It will intercept links for markdown and other non react pages and does a client side push state to avoid the browser having to refresh the page
- show “package.json” file that these plugins have been installed
14. Configure “gatsby-config.js”
- after installing these plugins we need to configure these files
- so in the plugins array add these below
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
`gatsby-plugin-catch-links`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages`,
},
},
`gatsby-transformer-remark`,
......
......
......
]
- run the script agian and make sure that you don’t have any errors
- Till now except blogs everything is eorking fine
15. Let us switch to graphQL
- we have a tool called “graphical”
- url: ‘localhost:8000/___graphql’
- this allows us to write graphQL queries
ex:
{
allMarkdownRemark {
edges{
node {
id
}
}
}
}
- To get all the files in the pages folder
- this will give data object that has all the files
- Edge is and array of our nodes and our nodes are our files
- id is complete path and file name of each file
16. Frontmatter
- when we use our query to get our markdown file we will use markdown-transformer
- frontmatter will give us frontmatter
- excerpt will give us the actual content
- so, we are using graphQL to grab our markdown files. This is what we will do in our markdown template
17. Blog page
- Let’s work on blog index page to show listing of blogs/posts
- create a new page/file called ‘blog.js’
import { graphql, Link } from 'gatsby';
import React from 'react'import Layout from "../components/layout"
import Seo from "../components/seo"
import "./common.css";const blogPage = ({data}) => (
<Layout>
<Seo title="Blog" description="Latest posts" />
<div className="blog">
<h1>Latest Posts</h1>
{ data.allMarkdownRemark.edges.map(post => (
<div className="blog-container" key={post.node.id}>
<h3 className="">{post.node.frontmatter.title}</h3>
<small className="">
Posted by {post.node.frontmatter.author} on {post.node.frontmatter.date}
</small>
<div className="blog-details-btn">
<Link to={post.node.frontmatter.path}>Read More</Link>
</div>
</div>
))
}
</div>
</Layout>
) export const pageQuery = graphql`
query BlogIndexQuery {
allMarkdownRemark {
edges{
node {
id
frontmatter {
path
title
date
author
}
excerpt
}
}
}
}
`;export default blogPage
18. Now we are going to query those posts inside this file
- so now in blog pages we can see all the posts
- if we click on ‘Read more’ then we will get 404 error
- so to resolve this we need to create a template
19. Blog template
- Create a folder in the source called “templates” and inside that create ‘blog-post.js’ file
- get the post from ‘markdownRemark’
- we will create a graphQL qury to get the ‘markdownRemark’ and other related data
import { graphql, Link } from 'gatsby';
import React from 'react';
import "./blog-post.css";export default function Template ({data}) {
const post = data.markdownRemark; return(
<div className="blog-post">
<Link to="/blog"> <span>←</span> Go Back</Link>
<hr />
<h1>
{post.frontmatter.title}
</h1>
<h4>posted by <strong>{post.frontmatter.author}</strong> on <small>{post.frontmatter.date}</small></h4>
<div dangerouslySetInnerHTML={{__html: post.html}}></div>
</div>
)
}export const postQuery = graphql`
query BlogPostByPath($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path} }) {
html
frontmatter {
path
title
date
author
}
}
}
`
- we will ise react’s dangerouslySetInnerHTML to set our HTML (i.e,
<div dangerouslySetInnerHTML={{__html: post.html}}></div>
)
- so this will allow to actually have HTML markup inside of it
20. Creating a query for “blog-post.js” (46:44)
- query has been added above
- Now, we will get a 404 error because even though we created a template and query it doesn’t know what the ‘post-one’ is to get to that post.
21. To resolve the above issue we will edit “gatsby.node.js”
- we need to use createPageAPI
const path = require('path');exports.createPages = async function ({ actions, graphql }) { const postTemplate = path.resolve('src/templates/blog-post.js') const { data } = await graphql(`
query {
allMarkdownRemark {
edges{
node {
html
id
frontmatter {
path
title
date
author
}
}
}
}
}
`)
data.allMarkdownRemark.edges.forEach(edge => {
const slug = edge.node.frontmatter.path
actions.createPage({
path: slug,
component: postTemplate,
context: { slug: slug },
})
})
}
22. Push to gitHub and deploy to netlify (If you want to)
23. “common.css” inside of pages
.jumbotron {
padding: 0 10p;
}
#page-title {
position: relative;
width: 100%;
color: white;
background-color: #1f4a6f;
font-family: "ubuntu", sans-serif;
overflow: hidden;
}
#title-image {
filter: alpha(opacity=80);
opacity: 0.8;
-moz-opacity: 0.8;
}
#page-title .container {
min-height: 70vh;
text-align: center;
padding: 3rem 1rem;
}
#page-title .article-title {
padding-bottom: 10px;
}
#page-title .article-title,
#page-title .article-subtitle {
text-shadow: 0px 0px 40px black;
}
.blog {
padding: 0 10px 10px 30px;
background-color: #e5ded8;
}
.blog h1 {
text-align: center;
padding: 20px 0 10px 0;
}
.blog .blog-container {
background: #fff;
border-radius: 5px;
box-shadow: hsla(0, 0, 0, .2) 0 4px 2px -2px;
font-family: "adelle-sans", sans-serif;
font-weight: 100;
margin: 48px auto;
width: 20rem;
margin: 0 auto;
padding: 1rem 1.5rem;
margin-bottom: 5px;
}
.blog .blog-container .blog-details-btn {
padding-top: 10px;
}
.blog .blog-container a {
color: #1f4a6f;
text-decoration: underline;
}
@media screen and (min-width: 480px) {
.blog .blog-container {
width: 28rem;
}
}
@media screen and (min-width: 767px) {
.blog .blog-container {
width: 40rem;
}
}
@media screen and (min-width: 959px) {
.blog .blog-container {
width: 50rem;
}
}
24. “blog-post.css” inside of pages
.blog-post {
padding: 10px 10px 10px 30px;
background-color: #e5ded8;
height: 100vh;
}
.blog-post a {
color: #1f4a6f;
}
25. Final output (Please cut me some slack for the design 😛)