Blog made by Gatsby
Tags: gatsby, react, blog
Published: 2020-05-27 01:25:43

<!-- vim-markdown-toc GFM -->

* [Background](#background)
* [Challenges](#challenges)
  * [Static Site Generation](#static-site-generation)
  * [Internationalization](#internationalization)
    * [Gatsby Plugin i18n](#gatsby-plugin-i18n)
    * [Translate Myself](#translate-myself)
  * [Markdown and JSX](#markdown-and-jsx)
    * [GraphQL](#graphql)
    * [Heading Anchor](#heading-anchor)
  * [Deployment](#deployment)
* [Implementation](#implementation)
  * [Gatsby Build System](#gatsby-build-system)
  * [GraphQL](#graphql-1)
  * [Internationalization](#internationalization-1)
    * [Page Level](#page-level)
    * [Field Level](#field-level)

<!-- vim-markdown-toc -->

Having read [this blog][why-self-host], I am convinced to host my personal blog. The most convncing point is that self-hosting blogs give more ways to monetize. Every developer is keen on monetizing his/her skills, just like me.

So the strategy has been made. Only tactics left undecided. There are 2 ways to build a self-hosted blog in general: using a blog builder such as WordPress, or build from "scratch". The difference between these 2 options is not actually well defined. I personally consider a tool requiring not web development skills as a blog builder.

As a half-frontend-developer, I decided to use a proper develper's framework for the job: [Gatsby][gatsby].

# Background

[Gatsby][gatsby] is a multi-page website generator based on React.js . The stack can be visualized as follows:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sg2usaqpsouq03tawd5d.png)

It can be seen that to use Gatsby, the user is expected to know HTML, CSS, JS, TS and React. Thus it is a great opportunity to self-learn frontend skills.

# Challenges

Before diving into this project, I had no knowledge of Gatsby as a framework. Thus the path I took to learn is also applicable for any other newbie developer who wish to learn Gatsby.

## Static Site Generation

As a Next.js developer, I am used to use server-side code during runtime. However it is forbidden for Gatsby as a Static Site Generator(SSG). This descrepancy has lead me to a dead end for many times. So these are the differences between SSG and SSR(Server Side Rendering):

| SSR                               | SSG                                        |
|-----------------------------------|--------------------------------------------|
| allow server side code at runtime | does not allow server side code at runtime |
| shipped with server executable    | shipped as plain html/css/... files        |
| dynamically generate pages        | requires rebuild to change page content    |

Based on the principle of SSG, 3 baselines need to be followed:

1. separate codes for buildtime and code for runtime
2. write runtime code as if writing in browser's dev tool
3. make sure all data can be statically fetched at buildtime

## Internationalization

Working in a non-English-speaking country, it is necessary to serve audience from both English background and Chinese background. Thus my blog needs to serve contents in both English and Chinese. This is one of the considerations I took when I decided to make my own blogger as no other blogger is popular in both China and the outside world.

Coming to the implementation part, there are several tools and examples available online that can guide me.

### Gatsby Plugin i18n

The first approach I tried was using a gatsby plugin. This is the best option for a non-blog site. I have used a similar tool for Next.js so this is not too hard to catch up. However this tool does not suit the needs of a blog site as it only provides field level translation. A blog utilizing field level translation is extremely difficult to maintain as all translation sources are kept in JSON files. A blog stored in JSON? No...

### Translate Myself

According to [the official guide](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n/), it is possible to achieve field level translation and page level translation at the same time.

Based on the guide, I made a [template][template] that bundles gatsby with mdx, page level translation and field level translation.

## Markdown and JSX

The most significant difference between a blog and a regular site is that blogs are served in virtually the same layout. For the ease of maintenance, it would be better to keep the source of the blogs in a stable file format that has not changed in the past 10 years. It would also be beneficial if the source and the output are very similar so that overhead is small, and it is visually clear during writing.

Combining the above considerations, markdown is the best option. Markdown standard is very stable. Although some improvements have been added in the past few years, all plugins keep a very good interoperability and good backward compatibility.

However, the original markdown is quite limited in terms of functionality. It's implicity constrains its potential, especially for the flexbility of UI. Hence an enhancement needs to be made.

JSX is a great tool for creating fancy UI. Using markdown as the backbone and occasionally decorated with JSX will provide a decent layout for blogs. Thus `gatsby-plugin-mdx` is chosen.

Having decided to adopt mdx, several issues came in the way.

### GraphQL

mdx in Gatsby allows page query. However, I personally do not like to use page query. Moreover, it is not usable in non-page components, which significantly limits its usage.

Sadly, component level query is not available in mdx as the JSX components in mdx are not compiled to retrieve static data during build time. Therefore the codes like `useStaticQuery` is left to run in runtime, where the query will not succeed.

### Heading Anchor

A very useful markdown enhancement is the anchor of the headings. This is one of the basic requirements for table of contents. However, `gatsby-plugin-mdx` is not shipped with this support.

According to [this blog][heading-anchor], this feature can be easily added.

## Deployment

The last problem is to host the site. There are generally 3 methods to choose:

1. buy a managed hosting service
2. host on a self-managed server
3. find a free hosting service

The option 1 and 2 are for lazy users who do not want to spend time digging the internet. I happen to know a hosting service for free: Github Pages.

Github Pages can be easily set up following the official guide. Bear in mind that there are 2 types of Page: project and personal. The one used for the purpose of this blog is the personal one.

# Implementation

Here I record the detailed implementation of the blog site I made. Basic knowledge of React.js is preassumed.

## Gatsby Build System

The first thing to learn is the build system of gatsby.

The source code is written in JSX and the output is static website. There are several fundamental issues here, most have been addressed by Gatsby itself, such as client side routing. One thing that requires special care is the data retrieval.

People from the background of server side coding may intuitively write codes that are left to run on server during runtime. This is a big no when using Gatsby, or any other [SSG](#static-site-generation) tool. Gatsby only allows 2 runtimes: buildtime and browser runtime.

During buildtime, all data are retrieved to create static HTML. During browser runtime, only browser API is available.

The codes for buildtime reside in the following locations:

1. page query and static query(GraphQL queries are run once at buildtime)
2. `gatsby-node.js`, `gatsby-config.js`, `gatsby-browser.js` and `gatsby-ssr.js` under the project's root directory

All other codes are compiled to static HTML at buildtime.

For details of the `gatsby-node.js` etc. see [the conceptual guid][concept].

## GraphQL

The usage of GraphQL in Gatsby is somewhat unusual. In traditional data retrieval modes, a client fires a query to a backend API and the backend respond with the required data. Naively I thought it happens during runtime. But Gatsby runs all GraphQL queries during buildtime. Then all the queries are stripped away from the output.

Therefore all the queries must be given all the required data and schema during buildtime. One of the main sources of data is `gatsby-config.js` and `gatsby-node.js`. check [the guide][concept] for details.

## Internationalization

To unify page level and field level internationalization, the following design is made.

### Page Level

Only mdx files are accepted for page level translation, such as posts and resume page.

The example source structure of a post is:

![i18n](https://dev-to-uploads.s3.amazonaws.com/i/zu94qdhchrge26yscsew.png)

All translations of the page are kept in one folder, which is named as the slug of the post. The file name represents the locales. All the information is extracted by codes in `gatsby-node.js` then is passed down to the corresponding pages.

As all the pages have the same layout, a template is passed to the `gatsby-plugin-mdx` that is applied to all post pages.

### Field Level

Field level translations are kept in a similar structure, where the file name represents the locale. A custom hook is used to retrieve those translations.

Check [my blog][my-blog] for the final result!

[gatsby]: https://www.gatsbyjs.org
[why-self-host]: https://bloggingexplorer.com/self-hosted-blog
[i18n]: https://github.com/angeloocana/gatsby-plugin-i18n
[template]: https://github.com/onichandame/gatsby-template
[heading-anchor]: https://johno.com/mdx-table-of-contents-components-in-gatsby
[concept]: https://www.gatsbyjs.org/docs/conceptual-guide/
[my-blog]: https://onichandame.com