Gabrijel Boduljak

Starting a blog

📅April 04, 2020 | ☕️10 minutes read

I have decided to start a blog, something I wanted to for a long time. I wanted something where I can write about topics I find interesting. During last two years at University, I have learned many interesting concepts, programming paradigms and algorithms about which I would like to write. Everything seemed like a perfect opportunity to start this project.

In this post I will go through technical stuff related to it. Firstly, I will go a bit deeper into project structure. Then I will write about how I used React to define templates for blog posts and I will finish off with a typical ‘new post’ scenario.

Technology

I have decided to use React and Gatsby. I wanted to build something ‘real’ using React because so far I had only hackathon experience with it.

Gatsby is a static page generator enabling you to write modern React getting transpiled to static website. Basically, you write (almost) a classic React application, but you get a well-optimised, out-of-the-box static website which includes code-splitting, lazy loading of images and similar static optimisations. Gatsby loads only the most important parts of the page, so the initial load is as fast as possible. Once loaded, Gatsby prefetches resources for linked pages so the next route on the website renders almost instantly. Moreover, it has a built-in CLI which allows you to use development server with hot-reloading, while making a production build using a single command. Cool, isn’t it? Learn more about Gatsby here.

Quite often, even a static website requires some external data. In the case of a blog website, we would like to write blog posts separately in some kind of folder structure. Gatsby has a built in API (called Gatsby Node) which allows you to specify from which folders you want Gatsby to load some kind of data (you specify which), and it allows you to query that data using GraphQL. Basically, it is going to (pre)process GraphQL queries in build time, and generate static content only. So in the case of a blog, we create content/blog folder and write each post using Markdown. Gatsby will take those posts according to specified folder structure, parse everything and allow you to query them using GraphQL in your React components.

It is a bit opinionated to use GraphQL by default, and you might wonder is it an overkill. However, you can use Gatsby without using GraphQL. Since it is well integrated in the platform, it was very easy to use it in this case, requiring no additional setup at all. After all, querying posts with GraphQL working almost out-of-the-box made many use cases nicer. Here is why Gatsby chose to use GraphQL?

Folder Structure & Config

Gatsby projects usually follow somewhat opinionated folder structure. Results of that are component, pages, templates subfolders inside src folder. However, there are some nice conventions related to it:

  • Components in src/pages become pages automatically and they have routes matching their names. For example, about.js inside pages will be mapped to /about.
  • Templates in src/templates will contain templates for programmatically generated pages. For example, I have blog-post.js template which will be used to display every post whose content originates from locally stored markdown file.

So it looks like:

blog
│   README.md
│   gatsby-browser.js
|   gatsby-config.js
│   gatsby-node.js
└───src
│   └───components
│       └───layout
│           | ....
│       │   bio.js
│       │   seo.js
|   └───pages
|   |   | index.js, ...
|   └───templates
|       | blog-post.js

Core components

gatsby-node (gatsby-node.js)

Gatsby node script is used to specify from where will website data be fetched and which routes will be automatically generated. Gatsby is configured to expose posts in the content folder via GraphQL interface. So to automatically generate routes for posts we query the content folder on the filesystem via built-in GraphQL interface and use Gatsby’s createPage to generate a page corresponding to a particular post. All posts are written as markdown files, and Gatsby will automatically compile them into the html.

This is how it is implemented:

  ...
  const blogPostTemplate = path.resolve(`./src/templates/blog-post.js`)
  const posts = queryResults.data.allMarkdownRemark.edges

  posts.forEach((post, index) => {
    const previousPost =
      index === posts.length - 1 ? null : posts[index + 1].node
    const nextPost = index === 0 ? null : posts[index - 1].node

    createPage({
      path: post.node.fields.slug,
      component: blogPostTemplate,
      context: {
        slug: post.node.fields.slug,
        previous: previousPost,
        next: nextPost,
      },
    })
  ...

blog post template (blog-post.js)

In order to display posts, it is required to define a dynamic post template. So all the posts share the simple blog-post.js template defined as a small React component:

const PostTemplate = ({ data, pageContext, location }) => {
  ...
  return (
    <Layout location={location} title={siteTitle}>
      <SEO
        title={post.frontmatter.title}
        description={post.frontmatter.description || post.excerpt}
      />
      <article>
        <header style={headerStyle}>
          <h1 style={titleStyle}>{post.frontmatter.title}</h1>
          <p> ... </p>
        </header>
        <nav>
         ...
        </nav>
        <section dangerouslySetInnerHTML={{ __html: post.html }} />
        <hr />
        <footer>
          <Bio />
        </footer>
      </article>
    </Layout>
  );

Tags page (pages/tags.js)

Tags page was the most interesting to implement. Although it is not a dynamic website, it was required to register it in the gatsby node. To get all the post tags, we query the filesystem for all posts and extract only the post tags in the following way:

export const pageQuery = graphql`
  query BlogPostOnlyTags {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark {
      edges {
        node {
          frontmatter {
            tags
          }
        }
      }
    }
  }

This query is in the same file as the Tags component which will use it.

Gatsby allows us to use the results of the pageQuery by injecting them into the component props. So to get all the post tags in a well-defined order it was sufficient to use a bit of Javascript in the Tags component.

  const posts = data.allMarkdownRemark
    ? data.allMarkdownRemark.edges.map(({ node }) => node)
    : []

  const tags = [
    ...new Set(
      posts
        .map(({ frontmatter }) => frontmatter.tags)
        .filter(tags => tags && tags.length)
        .reduce((allTags, tags) => allTags.concat(tags))
    ),
  ].sort()

The final component looks like:

const Tags = ({ data, location }) => {
  const siteTitle = data.site.siteMetadata.title
  const { headerStyle, titleStyle } = tagsTemplateStyles
  const posts = data.allMarkdownRemark
    ? data.allMarkdownRemark.edges.map(({ node }) => node)
    : []

  const tags = [
    ...new Set(
      posts
        .map(({ frontmatter }) => frontmatter.tags)
        .filter(tags => tags && tags.length)
        .reduce((allTags, tags) => allTags.concat(tags))
    ),
  ].sort()

  return (
    <Layout location={location} title={siteTitle}>
      <article>
        <header style={headerStyle}>
          <h1 style={titleStyle}>Tags</h1>
        </header>
        <section>
          <div>
            <ol>
              {tags.map((tag, index) => (
                <li key={index}>
                  <Tag tag={tag} />
                </li>
              ))}
            </ol>
          </div>
          <div>
            <Link to={"/"} rel="prev">
              ← Home
            </Link>
          </div>
        </section>
      </article>
    </Layout>
  )
}
export default Tags

Typical content flow

After all these technical details, it is worth noting how easy is to publish new posts and ensure there are visible as quickly as possible on the ‘production’ website. I have decided to use Netlify platform for hosting this static website. Netlify conveniently automatically (re)deploys the website after something is commited to the master branch. Here is a complete post publishing flow:

1. Add a new folder for post inside content
2. Write the post content in index.md
3. Ensure the post is good enough and it works
4. Push to the master branch of a repository
5. Wait until netlify automatically deploys the website

Hi👋! I am a final year Advanced Computer Science MSc student at 🎓University of Oxford. This is a place where I (will hopefully) write about topics I find interesting. I plan to write about my personal projects and learning experiences, as well as some random tech topics.
💻github | ✉️contact me | 📚my reading list | 👨‍💻my projects

Built with a lot of
© 2023