Thi's avatar
HomeAboutNotesBlogTopicsToolsReading
About|My sketches |Cooking |Cafe icon Support Thi
πŸ’Œ [email protected]

Gatsby descrete notes

Anh-Thi Dinh
GatsbyWeb DevSSG
Left aside
Gatsby is "Γ  la mode" and makes us feel that it's really fast. This note was made when I switched from Jekyll to Gatsby. I did not have much experience with React (neither JS) yet. You will find in this note not only things from Gatsby, but also from React and JS.
πŸ‘‰Β Build a website with Wordpress and Gatsby (part 1)
πŸ‘‰Β 
Build a website with Wordpress and Gatsby (part 2)

Installation on localhost

πŸ‘‰ Install npm and NodeJS (with npm). Check NodeJS + npm.
πŸ‘‰ Check
the official doc.
Then install gatsby globally,
πŸ‘‰ You should use starters, I choose official gatsby-starter-blog for the version 5 of my website.

Some tips for dev locally

πŸ‘‰ Woring with .env file | Gatsby Official doc.
πŸ‘‰ Note:
Using .env file in a NodeJS project
πŸ‘‰
Troubleshooting common errors | Gatsby Official doc.

Miscellaneous

  • React / Gatsby use JSX syntax. It's an XML/HTML-like syntax used by React that extends ECMAScript so that XML/HTML-like text can co-exist with JavaScript/React code.
  • Internal URLs: use Link (replaces <a> tag for internal links).
    • ⚠️
      You cannot use target='_blank' with <Link> because whenever you use internal links, they are always in the same window!
  • External URLs: use <a></a> as usual.
  • Use className instead of class=. E.g. className = "abc" or className = "abc xyz".
  • Inline CSS, <div style={{ color: "#ffff", paddingTop: "10px" }}></div>.
  • Date in Gatsby: {new Date().getFullYear()} or using moment.js.

Gatsby structure

  • Recipes -- a cookbook on how to build things, Gatsby style.
  • Gatsby Project Structure -- a tour of all the common folders and files.
  • Building with Components.
  • Layout Components
πŸ‘‰ Read this to understand the differences between Class Component and Functional Component (a.k.a. stateless). Below are 2 examples which give the same result.
  • Functional Component (stateless component): just a plain javascript function which takes props as an argument and returns a react element. You can't reach this.state inside it.
  • Component class: has a state, lifecycle hooks and it is a javascript class.
☝
πŸ’‘ The rule would be: if your component needs some data which cannot be passed as a prop, use class component to get that data. If you need to keep UI state in your component (expandable blocks) so it’s a good place to keep that info in a components state.

Understand props

When React sees an element representing a user-defined component, it passes JSX attributes to this component as a single object. We call this object "props" (properties). (ref)

Components

A page is basically,

Apply Bootstrap

πŸ‘‰ I prefer TailwindCSS for the version 5 of my website. Check next section.

Using TailwindCSS

Follow the official guide.

Using sass

Note: With Tailwind, you nearly don't need to write your own css rules.

Differences between layouts and templates

⚠️
These 2 concepts are not the core concepts of Gatby, we just need to use them for a good structure of our project. Their definitions are different, here are mine.
There are 2 separated folders /src/layouts and /src/templates.
  • layouts: usually the blueprint which doesn't contain graphql statements. For example, taxonomy.tsx (a blueprint for all categories, tags pages), base.tsx, page.tsx.
  • templates: "theme" for more specific types which usually contain grapql statements. For example, category.tsx, post.tsx, author.tsx, tag.tsx

Design base layout

What I need in the base layout:
  • A fixed navigation bar on top.
  • A fixed footer on bottom.
  • A flexible header.
  • A body wraper.

Design post / page templates

Their differences are just the width of the container.

Different Header for different page types

Add Navigation bar

Using react-bootstrap, create a file src/components/Navigation.js whose content is,
Then, in /src/Header.js
If you get stuck, check this video.

Using Font Awesome

Install (the free things) (if you have a pro license, read this) or this
To import everything in one place instead of importing each icon into each separate file, we'll create a Font Awesome library. Create src/components/fontawesome.js
Note that, an icon fas fa-money-bill will have name faMoneyBill from free-solid-svg-icons. In the case you wanna import an entire package,
In src/pages/index.js (for example),
πŸ’‘ Yes! fortawesome is correct!!!
πŸ’‘ If you have a problem in that the icon is firstly flashing big and then smaller, you need to set the configuration autoAddCss to false, (ref)

Google Fonts

Using typeface.js (search font in npmjs),
Rebuild to see the result!
Below is the old method (it didn't work well, it doesn't contain font-weight 600 for Open Sans without reason).

Insert images / photos

πŸ‘‰Β Gatsby Images

Adding markdown posts

Posts (.md files) are stored in /content/posts/. Install gatsby-transformer-remark
And add the following to gatsby-config.js
Create a file called post-1.md in content/posts/
...read this and this example for more...

Display site / post info on browser tab

Render html tag in a string

Instead of <p dangerouslySetInnerHTML={{ headerIntro }} />, you can use <p dangerouslySetInnerHTML={{__html: headerIntro }} />. If there is a html tag in headerIntro, e.g. "<i>Hello</i>" will be rendered as Hello.

JSX in Markdown

  • Download and use MDX plugin.
  • We have to put all mdx files in /src/pages. The mdx files will be renders automatically! That's why we need to indicate defaultLayouts in /gatsby-config.js (ref). I have tried to render mdx in /content/pages/ but it didn't work!
  • For an example of using graphql in mdx file, check /src/pages/about.mdx.
  • For a specific page, one can use props.pageContext.frontmatter.title to take the title of that page.
  • For writing pages, read this.

Create page template and markdown file

Suppose that you wanna create a page /about taking content from file /content/pages/about.md and it applies template /src/templates/page.js. All you need to do is following this post.
  1. First, add to /gatsby-config.
  1. Create /src/templates/page.js
  1. Create markdown file /content/pages/about.md.
  1. Modify /gatsby-node.js to tell gatsby to create a page /about from about.md using template page.js.

Errors?

πŸ‘‰ Requires...
πŸ‘‰ Cannot read property...
Above error comes from inserting images using query. To overcome this, we have to use StaticQuery which is introduced in Gatsby v2 (I don't know why it works!?) πŸ‘‰ The reason is that the (old) page query can only be added to page components (in my try, I add in Header.js component). StaticQuery can be used as a replacement of page query, it can be added to any component. (ref)
πŸ‘‰ Fail to build on Netlify Build script returned non-zero exit code: 127
  • Delete package-lock.json, don't include it and node_modules on git.
  • Remove either package.json or yarn.lock on Github (remove yarn).
  • "version": "0.1", is wrong, changing to "1.0.1" is OK.
  • Try to debug with netlify on localhost.
    • After installing, cd to gatsby-site and then run netlify dev.
    • Read more and more.
πŸ‘‰ Fail to build on Netlify Can't resolve '../components/Header' in '/opt/build/repo/src/components' for examples. πŸ‘‰ The problem comes from the original name of file Header.js is header.js. I renamed it to Header.js but it's still actually header.js (check the Github Desktop to see). You can change is to HeaderNew.js to fix the problem!
πŸ‘‰ If you wanna use adjacent react components, you have to put them inside <>..</> (React fragment) like below example,
This allows you to return multiple child components without appending additional nodes to the DOM.
πŸ‘‰ Warning: Each child in a list should have a unique "key" prop. You have to make sure that each child of a list in react component has a unique key. For example

References

  • Official documentation.
    • Examples.
    • Gatsby Docs -- Tutorials (step-by-step).
  • React Bootstrap -- get the components.
  • React Main Concepts -- understand some main concepts in React.
  • JSX in depth -- understand the syntax of JSX.
  • w3schools -- React Tutorial.
  • The Fullstack Tutorial for GraphQL
β—†Installation on localhostβ—‹Some tips for dev locallyβ—†Miscellaneousβ—†Gatsby structureβ—‹Understand propsβ—‹Componentsβ—†Apply Bootstrapβ—†Using TailwindCSSβ—†Using sassβ—†Differences between layouts and templatesβ—‹Design base layoutβ—‹Design post / page templatesβ—‹Different Header for different page typesβ—†Add Navigation barβ—†Using Font Awesomeβ—†Google Fontsβ—†Insert images / photosβ—†Adding markdown postsβ—†Display site / post info on browser tabβ—†Render html tag in a stringβ—†JSX in Markdownβ—†Create page template and markdown fileβ—†Errors?β—†References
About|My sketches |Cooking |Cafe icon Support Thi
πŸ’Œ [email protected]
1npm install -g gatsby-cli
2# Check version
3gatsby --version
1npx gatsby new gatsby-starter-blog <https://github.com/gatsbyjs/gatsby-starter-blog>
1# Restart cache
2gatsby clean
1// Class Component
2class MyComponentClass extends React.Component {
3  render() {
4    return <div>{this.props.name}</div>;
5  }
6}
1// Functional Component
2const MyStatelessComponent = props => <div>{props.name}</div>;
3// without JSX
4const MyStatelessComponent = props => React.createElement('div', null, props.name);
1import React from "react"
2function AboutPage(props) {
3  return (
4    <div className="about-container">
5      <p>About me.</p>
6    </div>
7  )
8}
9
10export default AboutPage
1import React from "react"
2export default (props) => {
3  return (
4    // ...
5  )
6}
7
8// or
9const AboutPage = (props) => (
10  // ...
11)
12export default AboutPage
1// in /scr/pages/index.js
2import Layout from "../layouts/layout"
1// in /scr/layouts/layout.js
2import "../styles/main.scss"
1// in /scr/styles/main.scss
2@import "layout";
1// in /scr/styles/_layout.scss
2// scss codes
1// in src/components/Header.js
2import React, { Component } from 'react'
3
4export default class Header extends Component {
5  render() {
6    const headerType = this.props.type
7    switch (headerType) {
8      case 'index':
9        return (
10          <>
11            <header className="idx-header header">
12              ...
13            </header>
14          </>
15        )
16      default:
17        return (
18          <>
19            <header className="header">
20              ...
21            </header>
22          </>
23        )
24    }
25  }
26}
1// in src/layouts/base.js
2import Header from "../components/Header"
3const Layout = ({ children, headerType='page' }) => {
4  return (
5    <>
6      <Header type='index' />
7      {children}
8    </>
9  )
10}
11export default Layout
12
1// in src/pages/index.js
2import Layout from "../layouts/base"
3const IndexPage = () => (
4  <Layout headerType='index'>
5    ...
6  </Layout>
7)
8export default IndexPage
1import React from 'react'
2import {Navbar, Nav, NavDropdown, Form, FormControl, Button} from 'react-bootstrap'
3
4export default (props) => (
5  // the codes from <https://react-bootstrap.netlify.com/components/navbar/#navbars>
6)
1import Navigation from '../components/Navigation'
2
3const Header = () => (
4  <header ..>
5    <Navigation></Navigation>
6    // other codes
7  </header>
8)
1npm i --save @fortawesome/fontawesome-svg-core @fortawesome/react-fontawesome @fortawesome/free-regular-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/free-brands-svg-icons
1// import the library
2import { library } from '@fortawesome/fontawesome-svg-core';
3
4// import your icons
5import { faHome, faFire, faEdit,  } from '@fortawesome/free-solid-svg-icons';
6
7library.add(
8  faHome, faFire, faEdit,
9);
1import { library } from '@fortawesome/fontawesome-svg-core';
2import { fab } from '@fortawesome/free-brands-svg-icons';
3
4library.add(fab);
1import '../components/fontawesome'
2import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
4<FontAwesomeIcon icon={'home'} /> // for 'faHome' or 'fas fa-home'
5<FontAwesomeIcon icon={['fab', 'github']} /> // for 'faGithub' or `fab fa-github`
1import { config  } from '@fortawesome/fontawesome-svg-core'
2import "@fortawesome/fontawesome-svg-core/styles.css"
3config.autoAddCss = false
1# install
2npm install --save typeface-open-sans
1# in gatsby-browser.js
2require('typeface-open-sans');
1npm install --save gatsby-plugin-prefetch-google-fonts
1// in /gatsby-config.js
2module.exports = {
3 plugins: [
4    {
5      resolve: `gatsby-plugin-prefetch-google-fonts`,
6      options: {
7        fonts: [
8          {
9            family: `Roboto Mono`,
10            variants: [`400`, `700`]
11          },
12          {
13            family: `Roboto`,
14            subsets: [`latin`]
15          },
16        ],
17      },
18    }
19  ]
20}
1npm install --save gatsby-transformer-remark
1plugins: [
2  {
3    resolve: `gatsby-source-filesystem`,
4    options: {
5      name: `posts`,
6      path: `${__dirname}/content/posts`,
7    },
8  },
9  `gatsby-transformer-remark`,
10  // there may be already others like this
11  {
12    resolve: `gatsby-source-filesystem`,
13    options: {
14      name: `images`,
15      path: `${__dirname}/src/images`,
16    },
17  },
18]
1---
2path: "/first-post"
3date: "2019-05-04"
4title: "My first blog post"
5---
1import Layout from "../layouts/base"
2import Helmet from 'react-helmet'
3const IndexPage = () => (
4  <Layout>
5    <Helmet title={`Thi | I failed my way to success`} />
6  </Layout>
7)
1# [email protected] requires a peer of [email protected]
2npm i [email protected] --save
3
4# [email protected] requires a peer of typescript@>=2.8.0
5npm i typescript --save
1# TypeError: Cannot read property 'fileName' of undefined
1return (
2    <>
3      <Navigation></Navigation>
4      <Header type={headerType} />
5      <span>Thi</span>
6    </>
7  )
1// error
2{links.map(link => (
3  <>
4    <span key={link.name}> Thi </span>
5    <Link key={link.name}> {link.name} </Link>
6  </>
7))}
1// but this
2{links.map(link => (
3  <span key={link.name}>
4    <span> Thi </span>
5    <Link> {link.name} </Link>
6  </>
7))}