Categories
Gatsby.js

Gatsby.js — 404 Page and SEO

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Add a 404 Page

We can create a 404 page by adding a 404.js file in the src/pages folder.

For example, we can write:

src/pages/404.js

import React from "react"

const NotFoundPage = () => {
  return <div>not found</div>
}

export default NotFoundPage;

to add this file.

Now we should see this page when we go to any unrecognized URL in production.

SEO

SEO is an important part of many sites.

Gatsby provides support for this with React Helmet.

We have to install React Helmet by running:

npm i react-helmet

Then we can write:

gatsby-config.js

module.exports = {
  siteMetadata: {
    title: "My Site",
    titleTemplate: "%s · The Real Hero",
    description: "Some website",
    url: "https://www.example.com",
    image: "/assets/dog.jpg",
    twitterUsername: "@occlumency",
  },
}

src/pages/index.js

import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, article }) => {
  const { pathname } = useLocation()
  const { site } = useStaticQuery(query)
  const {
    defaultTitle,
    titleTemplate,
    defaultDescription,
    siteUrl,
    defaultImage,
    twitterUsername,
  } = site.siteMetadata
  const seo = {
    title: title || defaultTitle,
    description: description || defaultDescription,
    image: `${siteUrl}${image || defaultImage}`,
    url: `${siteUrl}${pathname}`,
  }
  return (
    <Helmet title={seo.title} titleTemplate={titleTemplate}>
      <meta name="description" content={seo.description} />
      <meta name="image" content={seo.image} />
      {seo.url && <meta property="og:url" content={seo.url} />}
      {(article ? true : null) && <meta property="og:type" content="article" />}
      {seo.title && <meta property="og:title" content={seo.title} />}
      {seo.description && (
        <meta property="og:description" content={seo.description} />
      )}
      {seo.image && <meta property="og:image" content={seo.image} />}
      <meta name="twitter:card" content="summary_large_image" />
      {twitterUsername && (
        <meta name="twitter:creator" content={twitterUsername} />
      )}
      {seo.title && <meta name="twitter:title" content={seo.title} />}
      {seo.description && (
        <meta name="twitter:description" content={seo.description} />
      )}
      {seo.image && <meta name="twitter:image" content={seo.image} />}
    </Helmet>
  )
}

SEO.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string,
  image: PropTypes.string,
  article: PropTypes.bool,
}
SEO.defaultProps = {
  title: null,
  description: null,
  image: null,
  article: false,
}

const query = graphql`
  query SEO {
    site {
      siteMetadata {
        defaultTitle: title
        titleTemplate
        defaultDescription: description
        siteUrl: url
        defaultImage: image
        twitterUsername
      }
    }
  }
`

const IndexPage = () => {
  return <>
    <SEO />
    <div>hello world</div>
  </>
}

export default IndexPage

In gatsby-config.js , we have the website’s metadata.

The metadata is in the siteMetadata property.

title has the site’s title.

titleTemplate has the title template. %s is the placeholder for the title.

description has the site’s description.

url has the site’s URL. image has the site’s preview image.

twitterUsername has the Twitter username for the site.

Then in index.js , we create the SEO component.

It uses the Helmet component to add the title , titleTemplate , description , url , and twitterUsername from the site.siteMetadata property.

The data comes from the query object that we have at the bottom of the file.

We call the useStaticQuery hook with the query object to make the query.

We pass all the metadata into the Helmet component and they’ll be rendered in the head tag.

Conclusion

We can use the React Helmet package to render our site’s metadata in the head tag for SEO purposes.

Also, we can create a 404 page easily with Gatsby.

Categories
Gatsby.js

Gatsby.js — Render One or More Markdown Files

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Adding Markdown Pages

We can add pages from Markdown by adding the gatsby-transformer-remark and gatsby-source-filesystem plugins.

To do this, we write:

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ],
}

gatsby-node.js

exports.createPages = async ({ actions, graphql, reporter }) => {
  const { createPage } = actions
  const blogPostTemplate = require.resolve(`./src/templates/post.js`)
  const result = await graphql(`
    {
      allMarkdownRemark(
        sort: { order: DESC, fields: [frontmatter___date] }
        limit: 1000
      ) {
        edges {
          node {
            frontmatter {
              slug
            }
          }
        }
      }
    }
  `)

  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    createPage({
      path: node.frontmatter.slug,
      component: blogPostTemplate,
      context: {
        slug: node.frontmatter.slug,
      },
    })
  })
}

src/templates/post.js

import React from "react" { graphql } from "gatsby"
export default function Template({
  data,
}) {
  const { markdownRemark } = data
  const { frontmatter, html } = markdownRemark
  return (
    <div className="blog-post-container">
      <div className="blog-post">
        <h1>{frontmatter.title}</h1>
        <h2>{frontmatter.date}</h2>
        <div
          className="blog-post-content"
          dangerouslySetInnerHTML={{ __html: html }}
        />
      </div>
    </div>
  )
}
export const pageQuery = graphql`
  query($slug: String!) {
    markdownRemark(frontmatter: { slug: { eq: $slug } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        slug
        title
      }
    }
  }
`

src/content/post.md

---
title: My First Post
date: 2019-07-10
slug: /my-first-post
---
This is my first Gatsby post written in Markdown!

We add the gatsby-source-filesystem plugin to read the Markdown files from the src/content folder.

And we add the gatsby-transformer-remark plugin to transform the Markdown files to HTML.

Then in gatsby-node.js , we get the Markdown content with the allMarkdownRemark query.

We sort the entries by date with the sort values.

And we limit the number of entries to 1000.

And we specify that we return the slug in the result.

Then we call forEach to call the createPage function to create the pages.

The path is the URL path to access the page.

component has the template file path.

And context has the data that we want to display from the result entry.

In post.js , we have the query to get a single entry.

We get the entry with give slug with the markdownRemark query.

Then we get the html field to get the content.

frontmatter has the metadata for the page.

And we get that from the data prop’s markdownRemark property.

Finally, we render that in the JSX of Template .

Now we should see the post.md content displayed.

Add a List of Markdown Blog Posts

We can also add a page withn multiple posts.

To do this, we write:

src/page/index.js

import React from "react"
import { graphql } from "gatsby"
import { Link } from "gatsby"

const PostLink = ({ post }) => (
  <div>
    <Link to={post.frontmatter.slug}>
      {post.frontmatter.title} ({post.frontmatter.date})
    </Link>
  </div>
)

const IndexPage = ({
  data: {
    allMarkdownRemark: { edges },
  },
}) => {
  const Posts = edges
    .filter(edge => !!edge.node.frontmatter.date)
    .map(edge => <PostLink key={edge.node.id} post={edge.node} />)
  return <div>{Posts}</div>
}

export default IndexPage

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

We create the PostLink component to display a link for the post.

IndexPage has the posts.

We get the posts data from the data prop’s allMarkdownRemark.edges property.

And we map the entries to the PostLink component to render each entry with the PostLink component.

The pageQuery gets us the data for the data prop.

Conclusion

We can render one or more Markdown files in our Gatsby project.

Categories
Gatsby.js

Gatsby.js — Creating Pages from Data Programmatically

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Creating Pages from Data Programmatically

We can create pages from data programmatically with Gatsby.

For example, we can write:

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ],
  siteMetadata: {
    title: "My Homepage",
    description: "This is where I write my thoughts.",
  },
}

src/content/post.md

---
title: My First Post
date: 2019-07-10
path: /my-first-post
---
This is my first Gatsby post written in Markdown!

gatsby-node.js

const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  if (node.internal.type === `MarkdownRemark`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}

exports.createPages = async function ({ actions, graphql }) {
  const { data } = await graphql(`
    query {
      allMarkdownRemark {
        edges {
          node {
            fields {
              slug
            }
          }
        }
      }
    }
  `)
  data.allMarkdownRemark.edges.forEach(edge => {
    const slug = edge.node.fields.slug
    actions.createPage({
      path: slug,
      component: require.resolve(`./src/templates/post.js`),
      context: { slug },
    })
  })
}

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"

export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    <div>
      <h1>{post.frontmatter.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </div>
  )
}
export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
      }
    }
  }
`

First, we add the gatsby-transformer-remark and gatsby-source-filesystem plugins to read Markdown files and parse them to HTML.

We create the slug field with the createNode function.

We call createNodeField to create our slug.

With the if statement in onCreateNode , we only add the slug field if we query for MarkdownRemark entries.

Then in the createPages function, we make the query for the slug and then loop through each entry with forEach .

In the forEach callback, we call actions.createPage to add the pages from the entries returned.

path is set to the slug ,

component is set to the template file location.

context has the data we want to display in the template.

In post.js , we make the query for the post with the given slug.

Then we get the data from the data prop and render it in our JSX.

Now when we go to http://localhost:8000/post, we see:

My First Post

This is my first Gatsby post written in Markdown!

displayed.

Conclusion

We can create pages from data programmatically with Gatsby.

This way, we can create our pages from Markdown files and render them.

Categories
Gatsby.js

Gatsby.js — More GraphQL Queries

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Querying Data in Pages with GraphQL

We can query data in pages with GraphQL with Gatsby.

To do this, we write:

gatsby-config.js

module.exports = {
  siteMetadata: {
    title: "My Homepage",
    description: "This is where I write my thoughts.",
  },
}

src/pages/index.js

import React from "react"
import { graphql } from 'gatsby'

export const query = graphql`
  query HomePageQuery {
    site {
      siteMetadata {
        description
      }
    }
  }
`
export default function Home({ data }) {
  return (
    <div>
      Hello!
      {data.site.siteMetadata.description}
    </div>
  )
}

We have the website’s metadata in gatsby-config.js .

Then we can get the data in our page by using a GraphQL query.

We create the query with the graphql tag in index.js .

We use it to get the description of our site.

Then we can get the description in data prop with the data.site.siteMetadata.description property.

Now we see:

Hello!This is where I write my thoughts.

displayed.

Add Query Variables to a Page Query

We can add query variables to make our page query more flexible.

For example, we can write:

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"
export default function Template({ data }) {
  const { markdownRemark } = data
  const { frontmatter, html } = markdownRemark
  return (
    <div className="blog-post">
      <h1>{frontmatter.title}</h1>
      <h2>{frontmatter.date}</h2>
      <div
        className="blog-post-content"
        dangerouslySetInnerHTML={{ __html: html }}
      />
    </div>
  )
}
export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        title
      }
    }
  }
`

We add a query that takes the $path parameter.

And we specified that it’s a string.

Then we pass that into our markdownRemark query to search for posts with the given path.

Then in the Template component, we get the query result from the data prop,

frontmatter has the front matter, which is a post’s metadata.

And html has the HTML content.

StaticQuery

Another way to query for data on our website is with the StaticQuery component.

To use it, we write:

import React from "react"
import { graphql, StaticQuery } from 'gatsby'

export default function Home() {
  return (
    <div>
      <StaticQuery
        query={graphql`
          query HeadingQuery {
            site {
              siteMetadata {
                title
              }
            }
          }
      `}
        render={data => (
          <header>
            <h1>{data.site.siteMetadata.title}</h1>
          </header>
        )}
      />
    </div>
  )
}

We use the StaticQuery component with the query prop.

It has the GraphQL query we want to make as the value.

Then in the render prop, we can pass in a function to render the query result.

The data prop has the query result.

We query the same metadata from the gatsby-config.js .

Conclusion

Gatsby provides us with several ways to make queries to get data.

Categories
Gatsby.js

Gatsby.js — Conditional Queries and Create Pages

Gatsby is a static web site framework that’s based on React.

We can use it to create static websites from external data sources and more.

In this article, we’ll look at how to create a site with Gatsby.

Conditionals

We can add conditional operations with our GraphQL queries.

For example, we can with:

query GetBlogPosts($withDate: Boolean = false) {
  allMarkdownRemark(limit: 3, skip: 1) {
    edges {
      node {
        frontmatter {
          title
          date @include(if: $withDate)
        }
      }
    }
  }
}

We use the $withDate boolean value in our if operator.

Creating and Modifying Pages

We can use GraphQL queries to create pages in the gatsby-node.js file.

For example, we can write:

gatsby-node.js

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const result = await graphql(
    `
      {
        allMarkdownRemark(limit: 1000) {
          edges {
            node {
              frontmatter {
                path
              }
            }
          }
        }
      }
    `
  )
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const blogPostTemplate = path.resolve(`src/templates/post.js`)
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    const path = node.frontmatter.path
    createPage({
      path,
      component: blogPostTemplate,
      context: {
        pagePath: path,
      },
    })
  })
}

gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-transformer-remark`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `content`,
        path: `${__dirname}/src/content`,
      },
    },
  ]
}

src/templates/post.js

import React from "react"
import { graphql } from "gatsby"
export default function Template({ data }) {
  const { markdownRemark } = data
  const { frontmatter, html } = markdownRemark
  return (
    <div className="blog-post">
      <h1>{frontmatter.title}</h1>
      <h2>{frontmatter.date}</h2>
      <div
        className="blog-post-content"
        dangerouslySetInnerHTML={{ __html: html }}
      />
    </div>
  )
}
export const pageQuery = graphql`
  query($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        title
      }
    }
  }
`

We get the results with the createPages function in gatsby-node.js .

Then we loop through the items with the forEach call and call createPage to create the posts.

We reference post.js , which is the template component.

And then we call createPage and set the template as the component.

context has data we want to display on our page.

We access that in the data prop of the Template component.

Removing Trailing Slashes

We can remove trailing slashes from the path when we create the page.

For instance, we can write:

const replacePath = path => (path === `/` ? path : path.replace(//$/, ``))

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage, deletePage } = actions
  const result = await graphql(
    `
      {
        allMarkdownRemark(limit: 1000) {
          edges {
            node {
              frontmatter {
                path
              }
            }
          }
        }
      }
    `
  )
  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const blogPostTemplate = path.resolve(`src/templates/post.js`)
  result.data.allMarkdownRemark.edges.forEach(({ node }) => {
    const path = node.frontmatter.path
    const page = {
      path,
      component: blogPostTemplate,
      context: {
        pagePath: path,
      },
    }
    const oldPage = Object.assign({}, page)
    page.path = replacePath(page.path)
    if (page.path !== oldPage.path) {
      deletePage(oldPage)
      createPage(page)
    }
  })
}

We check if the oldPage.path is equal to page.path .

If they aren’t equal, then we call deletePage to delete the old page and call createPage to add the new page.

Conclusion

We can make conditional queries and create pages from GraphQL queries with Gatsby.