All The Dumb Mistakes I Made Building My First Gatsby Site

Gatsby is great, but there's a lot to learn about in the Gatsby ecosystem. Check out mistakes to avoid when building your first Gatsby site.

November 28, 2018

Another post in my funemployment blog series. So anytime I’m on #funemployment (which face it, has been about once a year lately, sighhhh. I’m ready to find my forever home that’ll love me for me) I like to explore new tech I’ve been too heads down getting shit done to play with. This time up it’s React and Gatsby. I could just start building React from scratch, but I’m lazy and

Ain't nobody got time for that meme

Hence, my interest in Gatsby.

Formerly I’ve been deep in Angularland - Kansas City is a super .NET heavy town and love to pair Angular with it. I’ve been ok with this, Angular is a great framework to operate in and has a lot of structure in place, meaning I don’t have to make as many decisions, which I’m all about. Speaking of hating making decisions and being lazy, this was probably my main hesitancy to leaving Angularland …

upping a site via Angular CLI

Damn do I love the Angular CLI and automatically generating and injecting components where they need to be with limited keystrokes. (and aliasing commands to use ponysay ) But about 30 seconds into building my first Gatsby site I realized this wasn’t a concern I needed to have.

As a fender, my primary app concerns are typically around routing, crafting reusable layouts, creating minified builds, how I’m binding to the dom, and unit tests that won’t take me more than minutes to set up. Angular handles all these, shoutout for excellent webpack implementation, but to my surprise and delight, Gastby does a wonderful job managing these concerns as well!

So for my first venture into Gatsbytopia, I wanted to refactor my personal website, previously in Jekyll, to a Gatsby blog and do some performance refactoring cause my simple static site was S-L-O-W. I also wanted to host my resume on my website, vs just linking to it’s previous home https://registry.jsonresume.org/ which went down at a few very inopportune times during my job hunt. merp.

Not understanding React component types

There needs to be a “So you missed the React boat a few years ago cause you were too busy trying to solve diversity in tech issues and working at companies not using React and are struggling to keep up so no one thinks you’re a moron” book. So here’s my best attempt at a crash course for those traipsing behind me. We have functional(stateless) and class-based(stateful) components,

Functional

We can pass our data around using props, and nest these babies all day long. Pretty simple looking. Name function. Insert markup. Export function. Triumph.

  function MyComponent(props) {
    return <div>{props.name}</div>
  }

These came in handy when I got sick of writing a hero component more than once.

  // /pages/index.js

  import Hero from '../components/Hero'
  import get from 'lodash/get'

  class Index extends React.Component {
    render() {
      const image = siteUrl + get(this, 'props.data.newheadshotImage.sizes.src');

      return (
        <Layout location={this.props.location} title={siteTitle}>
          <Hero headerImage={this.props.data.headerImage}
            heading="Jennifer Wadella"
            text="Often described as a force of nature, lol jk Jennifer sucks at coding" />
          </div>
        </Layout>
      )
    }
  }

  export default Index
  // /components/Hero.js

  import React from 'react'
  import Img from "gatsby-image";
  import styled from 'styled-components'

  import { FaAngleDoubleDown } from 'react-icons/fa';

  const Hero = props => (
    <header className="header intro">
      <Img
         title="Header image"
         alt="Hero banner"
         sizes={props.headerImage.sizes}
         style={{
            position: "absolute",
            left: 0,
            top: 0,
            width: "100%",
            height: "100%"
          }}
       />
       <div className="container intro-body">
         <div className="row justify-content-center">
           <div className="col-md-12">
             <h1 className="brand-heading">{props.heading}</h1>
             <p className="intro-text">{props.text}</p>
             <DownArrow href="#about" aria-label="about Jennifer">
                 <FaAngleDoubleDown />
              </DownArrow>
           </div>
         </div>
       </div>

    </header>
  );

  export default Hero

Class-Based

I won’t go to in detail here, because for my first static blog site literally the only activity is toggling the dropdown navigation(I FIXED IT MAT ARE YOU HAPPY). But here is my stateful toggling. Enjoy. Cause it’s not like we’ve been dealing with this stupid pattern for the last 8 years amirite?

  class Navigation extends React.Component {
    constructor(props) {
      super(props);
        this.toggleNavbar = this.toggleNavbar.bind(this);
        this.state = {
          collapsed: true,
        };
    }

    toggleNavbar() {
      this.setState({
        collapsed: !this.state.collapsed,
      });
    }
    render() {
      const classDropdownMenu = 'navbar-collapse' + (this.state.collapsed ? ' collapse' : '')

      return (
        <button onClick={this.toggleNavbar}>
          <span className="navbar-toggler-icon"></span>
        </button>
        <div className={classDropdownMenu} id="navbarSupportedContent">
          ... my nav stuff
        </div>
      )
    }
  }

  export default Navigation

Couple things to keep in mind - your class-based component must have a render method. At least, if you want anything to render on the page. In those cases where you find yourself mapping over nested cases, you can hop between getting your JavaScript on and returning markup, like dis:

  import React from 'react'
  import get from 'lodash/get'

  class Speaking extends React.Component {
    render() {
      const confs = get(this, 'props.data.allSpeakingJson.edges')
      const image = siteUrl + get(this, 'props.data.headerImage.sizes.src');

      return (
        //wooo markup(well, JSX, but trying to use n00b friendly ideas)
        <div>
        <Hero headerImage={this.props.data.headerImage}
          heading="Speaking Engagements"
          text="" />
        <h1>Upcoming & Past Appearances</h1>
        <FlexGrid>
          //wooo JavaScript
          {confs.reverse().map(({ node }) => {
            let image = require('../assets/img/speaking/' + node.image);
            return (
              //wooo moar markup
              <Conference key={node.date}>
                <h2>{node.conference}</h2>
                <ul>
                  //ermergherd moar javascripts
                  {node.talks.map((talk) => {
                    return (
                      //stfu jennifer we get it now
                      <li key={talk.title}> {talk.title}</li>
                    )
                  })}
                </ul>
              </Conference>
            )
          })}
        </FlexGrid>
        </div>
      )
    }
  }

  export default Speaking

Making dumb assumptions about graphQL

“Well, Jennifer, maybe if you read the docs thoroughly … ”

“SHUT UP I LIKE DOING IT THE HARD WAY”

The main dumb assumption I made was that all the graphQL magic started from the gatsby-node.js. There IS plenty of magic there rending our pages dynamically using MarkdownRemark(handling my routing concerns), but for my purposes I just cared about generating content from json files I converted from the yaml files of my jekyll site for listing my talks and press articles, and those graphQL queries could just live in the pages that cared about the data.

Gatsby-transformer-json makes this super easy! I created a directory called data, and for each type of data I cared about, I created a folder and json file named appropriately - /data/speaking/speaking.json

 // gatsby-config.js

 plugins: [
    `gatsby-transformer-json`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        path: `./src/data/`,
      },
    },
  ],
  // pages/speaking.js
  import React from 'react'
  import get from 'lodash/get'

  class Speaking extends React.Component {
    render() {
      const talks = get(this, 'props.data.allTalksJson.edges')

      return (
          <div >
            <h2>Talks</h2>
            <FlexGrid>
              {talks.map(({ node }) => {
                return (
                  <Talk key={node.title}>
                    <h3>{node.title}</h3>
                    <p>{node.abstract}</p>
                    <p><a target="_blank" href={node.slides} aria-label={"view " + node.title + " slides"}>Slides</a></p>
                  </Talk>
                )
              })}
            </FlexGrid>
          </div>
      )
    }
  }

  export default Speaking

  export const pageQuery = graphql`
    query {
      allTalksJson {
        edges {
          node {
            title
            abstract
            slides
          }
        }
      }
    }
  `

Also, I really struggled with attempting to debug graphQL issues via my command line output. Save yourself the headache and just look at the history on http://localhost:8000/___graphql, and when in doubt, assume you missed a comma or field in your json. Also worth noting, if you’re looking for an image with an image optimizer, let’s say like, a header for your blog post, and graphQL can’t find it relative to it’s location you’ll get a super ambiguous error.

Not sure if you need graphQL? Well, it comes with Gatsby, but just in case, check out my friend Mat’s site https://shouldiusegraphql.com/

Putting my css in all the wrong places

Ok, really just one wrong place, but the header was funnier that way. Being the jackass I am, familiar with my angular.json config having a place for importing my base css, I just looked for the highest file level to slap my less file in, this case being /components/Layout.js. Makes sense, all my pages are using Layout.js with their content nestled within, and I’m too chickenshit to leave my BootstrapTM css file out of my development process. On a scale of 1-needtherapyASAP where does framework co-dependency fall?

  // /components/Layout.js
  import React from 'react'
  import Navigation from './Navigation'
  import Footer from './Footer'

  require('bootstrap/dist/css/bootstrap.min.css')
  require('../assets/css/index.less');

  class Layout extends React.Component {
    render() {
      const { location, title, children } = this.props
      const rootPath = `${__PATH_PREFIX__}/`

      return (
        <div style={{backgroundColor: "#000", color: "#fff"}}>
          <Navigation />
          <div role="main">
            {children}
          </div>
          <Footer />
        </div>
      )
    }
  }

  export default Layout

This approach worked fine for my local development, but as soon as I deployed to Firebase, shit hit the fan. None of my base styles loaded until I navigated to a different route. Wat. A bit of stackoverflow/github issue browsing later I cried to Jason Lengstorf about my dumbassery and he pointed out that Layout unmounts and remounts on every page, and the proper home for my styles was in the gatsby-browser.js file. Yaaaay styles for Jennifer.

  // /gatsby-browser.js
  require('bootstrap/dist/css/bootstrap.min.css')
  require("prismjs/themes/prism-okaidia.css")
  require('./src/assets/css/index.less')
  // - thanks, Jennifer, we love our new home! 🤗

Using styled-components vs. Emotion

Ok, technically this isn’t my fault since styled-components was in my Gatsby Starter. And I didn’t actually use Emotion until my 2nd Gatsby site, but now that I am I luuuurve it and am never letting go. At least until something better comes along. Styled-components and Emotion’s styled wrapper have a very similar API, so if you want to swap them out right after reading this it should be pretty painless! The big appeal here is performance, but there are a few other nifty things that caught my eye, like being able to use another component as a selector. Gasp! Check this out, fair reader, I may finally be able to break my reliance on Bootstrap!

styled-components

  // /pages/press.js
    import styled from 'styled-components'
    import get from 'lodash/get'

    class Press extends React.Component {
      render() {
        const press = get(this, 'props.data.allPressJson.edges')
        return (
          <Layout location={this.props.location} title={siteTitle}>
            <PressContainer>
              <div className="container">
                  <div className="row press-article">
                    <div className="col"><h1>In The Media</h1></div>
                  </div>
                  {press.reverse().map(({ node }) => {
                    return (
                      <div className="row press-article" key={node.title}>
                          ... moar content here
                      </div>
                    )
                  })}
              </div>
            </PressContainer>
        </Layout>
        )
      }
    }

    export default Press

    const PressContainer = styled.div`
      text-align: left;
      padding-top: 20px;
      font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
      background-color: #fff;

      @media(min-width: 1200px){
        .container {
          max-width: 960px;
        }
      }
    `

emotion

  // /pages/press.js
  import styled from '@emotion/styled'
  import get from 'lodash/get'

  class Press extends React.Component {
    render() {
      const press = get(this, 'props.data.allPressJson.edges')
      return (
        <Layout location={this.props.location} title={siteTitle}>
          <PressContainer>
            <BSContainer>
                <div className="row press-article">
                  <div className="col"><h1>In The Media</h1></div>
                </div>
                {press.reverse().map(({ node }) => {
                  return (
                    <div className="row press-article" key={node.title}>
                        ... moar content here
                    </div>
                  )
                })}
            </BSContainer>
          </PressContainer>
      </Layout>
      )
    }
  }

  export default Press

  const BSContainer = styled.div`
    width: 100%;
    padding-right: 15px;
    padding-left: 15px;
    margin-right: auto;
    margin-left: auto;
  `

  const PressContainer = styled.div`
    text-align: left;
    padding-top: 20px;
    font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
    background-color: #fff;

    @media(min-width: 1200px) {
      ${BSContainer} {
        max-width: 960px;
      }
    }

Not optimizing my images like a damn neanderthal

What does one do when you’re too lazy to perfectly style your website typography? Slap a bunch of images on there to distract from your aesthetic failure, obviously. (sorry to my Graphic Design professors) One quick look at Lighthouses’ eval of my site’s performance was plenty to motivate me to find a nice way to optimize. Enter gatsby-image ! TBH, there’s a really good article about using gatsby-image here, but if you are entertained by my jackassery, here’s mah code.

  // gatsby-config.js

  plugins: [
    // mah other plugins
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `img`,
        path: `${__dirname}/src/assets/img/`
      }
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`
  ]
  // /pages/index.js

  import React from 'react'
  import { graphql } from 'gatsby'
  import Img from "gatsby-image";


  class Index extends React.Component {
    render() {
      return (
        <Img
           title="Jennifer mentoring"
           alt="Jennifer mentoring"
           sizes={this.props.data.mentorImage.sizes}
           style={{
              position: "absolute",
              left: 0,
              top: 0,
              width: "100%",
              height: "100%"
            }}
         />
      )
    }
  }

  export default Index

  export const pageQuery = graphql`
    query HeaderImageQuery {
      mentorImage: imageSharp(original: {src: { regex: "/mentor/" }} ) {
        sizes(maxWidth: 1240 ) {
          ...GatsbyImageSharpSizes
        }
      }
      site {
        siteMetadata {
          title
          description
          siteKeywords
          siteUrl
        }
      }
    }
  `

Fear not, you can also use this for images within a query! Quick reminder that graphQL will throw strange hard to figure out errors if your image files can’t be found, whether misspelled, improper extension otherwise. My main complaint is gifs are not supported, but I’m sure they’ll fix this grevious mistake at some point.

  import React from 'react'
  import { graphql } from 'gatsby'
  import get from 'lodash/get'

  class Blog extends React.Component {
    render() {
      const posts = get(this, 'props.data.allMarkdownRemark.edges')

      return (
        <div>
          <h1>On The Blog</h1>
          {posts.map(({ node }) => {
            const title = get(node, 'frontmatter.title') || node.fields.slug
            return (
              <div className="row blog-article" key={node.fields.slug}>
                <div className="col">
                <Img
                   title={title}
                   alt={title}
                   sizes={node.frontmatter.thumbnail.image.sizes}
                 />
                </div>
                <div className="col-9">
                  <h2>
                    <Link style={{ boxShadow: 'none' }} to={node.fields.slug}>
                        {title}
                    </Link>
                    <p>{node.frontmatter.date}</p>
                    <p dangerouslySetInnerHTML={{ __html: node.excerpt }} />
                  </h2>
                </div>

              </div>
            )
          })}
        </div>
      )
    }
  }

  export default Blog

  export const pageQuery = graphql`
    query {
      allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
        edges {
          node {
            excerpt
            fields {
              slug
            }
            frontmatter {
              date(formatString: "MMMM DD, YYYY")
              title
              thumbnail{
                image:childImageSharp {
                  sizes(maxWidth: 2000 ) {
                    ...GatsbyImageSharpSizes
                  }
                }
              }
            }
          }
        }
      }
    }
  `

Anyway, I hope this is a helpful guide towards anyone getting started in Gatsby and makes you realize we all make dumb mistakes, the important thing is that you learn from them. 💖

Final thoughts? UGHHHH ARIANA DROP THAT DAMN MUSIC VIDEO ALREADY. I NEEEEEEED ITTTTTT.