vincentdnl

How I created a comics blog in 2 hours with Gatsby

January 24th, 2020

I've had a comics blog in the past and kinda lost the motivation to draw frequently. Now I'm back at it, with simpler and quicker drawings. I started posting on my Instagram which is great and might be enough for what I intend to do. But I like the idea of having what I do under a domain I own.

That's what I decided to create my comics blog: https://gavé.cool (I might develop more on the hassle of having a URL with special characters in it...)

My requirements were:

  • I didn't want to spend a lot of time setting it up. In fact, the quicker the better.
  • I didn't want it to be too expensive (except for the domain name)
  • I wanted it to be minimalist, ie none of that bloat you have with things like Wordpress.
  • I wanted to have control over it

I used Gatsby because it addresses a lot of these issues and I already used it successfully in the past. Gatsby is a static site generator. Compared to Wordpress, using Gatsby requires you to be a developer or at least know a bit of programming. I wouldn't recommend it to someone who isn't at least a bit confident with technology, at least at the moment.

With Gatsby, I had control over where I deploy it. It's a static site generator so every CDN will do the trick. I chose Netlify as my host as it has a very generous free tier and the deployment is very easy once you linked your Github or Gitlab repository to it.

I chose the most popular Gatsby starter called gatsby-blog-starter. I opened my favorite terminal and one gatsby new gave-cool https://github.com/gatsbyjs/gatsby-starter-blog command later I was set to start.

A blog that displays comics on the index

On comics blogs, either the last comic is displayed on the front page, or all comics in chronological order. I will go with the later.

The boilerplate I used already has this kind of mechanism in src/pages/index.js where you loop on every node that is published:

{posts.map(({ node }) => {
    //...

posts is given by the following GraphQL query (go check the repository to see the whole file):

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

The fields are what appear on top of the markdown files in the content/blog folder. I kept that structure for my comics blog. Each drawing will be a folder named by the slug of the post. It will contain a drawing and a index.md file that will contain some headers. Here is what it looks like on my blog:

---
title: Bon appetit
date: "2020-01-23T20:00:00.000Z"
drawing: "./bon-appetit.png"
---

That will be the first post and the first drawing posted on the blog. The other blog posts will follow the same model.

Let's update the GraphQL pageQuery to get the custom field:

//...

export const pageQuery = graphql`
    query {
        hero: file(absolutePath: { regex: "/hero-gave-cool.png/" }) {
            childImageSharp {
                fluid(maxWidth: 800, quality: 100) {
                    ...GatsbyImageSharpFluid
                }
            }
        }
        site {
            siteMetadata {
                title
            }
        }
        allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
            edges {
                node {
                    excerpt
                    fields {
                        slug
                    }
                    frontmatter {
                        date(formatString: "MMMM DD, YYYY")
                        title
                        drawing {
                            childImageSharp {
                                fluid(maxWidth: 400, quality: 100) {
                                    ...GatsbyImageSharpFluid
                                }
                            }
                        }
                    }
                }
            }
        }
    }
`

Now let's use it in the BlogIndex component (it requires that you import Img from the gatsby-image module):

//...
<section>
    <Img fluid={node.frontmatter.content.childImageSharp.fluid}/>
</section>
//...

That's it, now our index displays just the title, the date, and the image!

The blog post page

Now we want a page that contains only one post. The main purpose of this page is that people can link to only one drawing with a URL containing the slug of the post. Let's edit the templates/blog-post.js file so that the drawing displays first.

Same goes for the GraphQL query:

//...
            frontmatter {
                title
                date(formatString: "MMMM DD, YYYY")
                drawing {
                    childImageSharp {
                        fluid(maxWidth: 400, quality: 100) {
                            ...GatsbyImageSharpFluid
                        }
                    }
                }
            }
//...

And the section in the React component becomes:

//...
<section>
    <Img fluid={post.frontmatter.content.childImageSharp.fluid}/>
    <div dangerouslySetInnerHTML={{__html: post.html}}/>
</section>
//...

That's it, the post now fits the requirements.

A header image

I like it when comics blogs have a nice hero image on top. I drew something super fast and edited the componets/layout.js file and redefined the header so that it contains only one image that gets you back to the landing page when clicked:

//...
            <Link to={`/`}>
                <Img fluid={hero.childImageSharp.fluid} alt="" style={{                        maxHeight: `25rem`}}/>
            </Link>
//...

On top of the GraphQL query in index.js we add:

//...
    query {
        hero: file(absolutePath: { regex: "/hero-gave-cool.png/" }) {
            childImageSharp {
                fluid(maxWidth: 800, quality: 100) {
                    ...GatsbyImageSharpFluid
                }
            }
        }
//...

And then just pass the image as a props:

            <Layout location={this.props.location} title={siteTitle} hero={data.hero}>

That's it for the hero image.

Conclusion

The whole process from getting the boilerplate to hosting it on Netlify took me less than two hours if you don't account for DNS propagation. You can view the result here even if most of the drawings will be in french. Multiple improvements can be made, like for example adding an alt field to the posts that would allow for better SEO, adding Google Analytics and Google Search Console.

The cost of having a static site running is also super cheap. For most hosting solutions (especially when there is a database involved like in Wordpress), the cost is always something different from zero. If you are doing a personal blog, a static website is probably enough. It's also a lot faster than any solution involving a database.

Gatsby being used on top of React makes it very easy to modify and it feels like it is the right abstraction for pages.