For some time now I’ve been trying to publish quick tips about Gatsby on Twitter, because the community loves such short, useful tips. Accordingly, I had already created a Twitter moment back then – but why only content from me? On January 6th of this year I also called the community to share their quick tips (with great success). As promised, this blogpost is supposed to be a collection of these (and other) tips. You can also tag me on Twitter if you want to add your tip to the moment!
Some tips are also accompanied by CodeSandbox examples.
#Date of last Build
Gives you the date for when you last used gatsby build. In the case of e.g. Netlify that would be the last time you deployed your site (hence buildTime). Please use this query sparingly (e.g. not in your footer component that you share with every page) as otherwise on every build the queries would be invalidated (since the time changed) and you’d unnecessarily rebuild all pages.
import * as React from "react"import { graphql } from "gatsby"
const IndexPage = ({ data }) => { return ( <main> <p>This site was last built on:</p> <p>{data.site.buildTime}</p> </main> )}
export default IndexPage
export const query = graphql` query { site { buildTime(formatString: "DD/MM/YYYY") } }`
#Date of last modification
For open source docs it’s good to know when a document was last modified. You can get that information via the parent
if you use local files like Markdown, MDX, YAML etc. Use the ___graphql
endpoint to explore that information on your own project.
query { allMdx { nodes { parent { ... on File { modifiedTime(formatString: "MM/DD/YYYY") } } } }}
#Same Source, different Queries
When you define two (or more) gatsby-source-filesystem
entries in your config you can filter your GraphQL queries to only get the result of one source.
module.exports = { plugins: [ { resolve: "gatsby-source-filesystem", options: { name: "assets", path: `${__dirname}/src/assets`, }, }, { resolve: "gatsby-source-filesystem", options: { name: "logos", path: `${__dirname}/src/logos`, }, }, "gatsby-transformer-sharp", "gatsby-plugin-sharp", ],}
Now place one image file in the src/logos
folder and two or more image files in the src/assets
folder. This should show the difference between the file
and allFile
query (of course you can put any number of files into one folder). You can address both folders individually, as shown in the following example:
import * as React from "react"import { graphql } from "gatsby"import { GatsbyImage, getImage } from "gatsby-plugin-image"
const IndexPage = ({ data }) => { return ( <main> <p>First image (logo):</p> <GatsbyImage image={getImage(data.logo)} /> <p>Assets images (two):</p> <div style={{ display: "flex", flexWrap: "wrap" }}> {data.assets.nodes.map((img) => ( <GatsbyImage image={getImage(img.childImageSharp)} /> ))} </div> </main> )}
export default IndexPage
export const query = graphql` query { logo: file(sourceInstanceName: { eq: "logos" }) { childImageSharp { gatsbyImageData(width: 200, quality: 100) } } assets: allFile(filter: { sourceInstanceName: { eq: "assets" } }) { nodes { childImageSharp { gatsbyImageData(width: 200, quality: 100) } } } }`
#Gatsby’s reporter
API
You can not only use async/await in gatsby-node.js
but also use Gatsby’s reporter API. Use this to throw errors. You should use panicOnBuild
to only stop the process on gatsby build
(so that you can debug it with GraphiQL).
const yourTemplate = require.resolve("./src/templates/yourTemplate.js")
exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions
const result = await graphql(` { ...your GraphQL query } `)
if (result.errors) { reporter.panicOnBuild("Error while running GraphQL query.") return }
result.data.yourNode.nodes.forEach((node) => { // createPage function })}
#Previous/Next - Links
Most of the time you want to show a previous/next post under your blog post to keep the visitor reading. For that you can use pageContext
.
const bookTemplate = require.resolve("./src/templates/book.js")
exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions
const result = await graphql( graphql(` { allBooksYaml(sort: { fields: [title], order: ASC }) { nodes { slug title } } } `) )
if (result.errors) { reporter.panicOnBuild("Error while running GraphQL query.") return }
const books = result.data.allBooksYaml.nodes
books.forEach((node, index) => { // Set the prev/next variable for every node so // that you can directly access slug & title
const prev = index === 0 ? null : books[index - 1] const next = index === books.length - 1 ? null : books[index + 1]
createPage({ path: node.slug, component: bookTemplate, context: { slug: node.slug, prev, next, }, }) })}
import * as React from "react"import { graphql, Link } from "gatsby"
import Layout from "../components/layout"
const BookTemplate = ({ data: { booksYaml }, pageContext: { prev, next } }) => { return ( <Layout> <h2>{booksYaml.title}</h2> <div> {prev && ( <div> <span>Previous</span> <Link to={prev.slug}>{prev.title}</Link> </div> )} {next && ( <div> <span>Next</span> <Link to={next.slug}>{next.title}</Link> </div> )} </div> </Layout> )}
export default BookTemplate
export const pageQuery = graphql` query BookBySlug($slug: String!) { booksYaml(slug: { eq: $slug }) { title content } }`
#Random Links
In contrast to the previous tip you can also show two (or more) random posts by changing gatsby-node.js
.
const _ = require("lodash")
const prevNext = (list, item) => { // Create a random selection of other posts (excluding the current post) const filterUnique = _.filter(list, (input) => input.slug !== item.slug) const sample = _.sampleSize(filterUnique, 2)
return { prev: sample[0], next: sample[1], }}
const bookTemplate = require.resolve("./src/templates/book.js")
exports.createPages = async ({ graphql, actions, reporter }) => { const { createPage } = actions
const result = await graphql( graphql(` { allBooksYaml(sort: { fields: [title], order: ASC }) { nodes { slug title } } } `) )
if (result.errors) { reporter.panicOnBuild("Error while running GraphQL query.") return }
const books = result.data.allBooksYaml.nodes
books.forEach((node) => { const { prev, next } = prevNext(books, node)
createPage({ path: node.slug, component: bookTemplate, context: { slug: node.slug, prev, next, }, }) })}
#Download Images from a CDN
If your images are hosted on a CDN and you want to use gatsby-plugin-image
, you can download them using the createRemoteFileNode
helper. Then add the image to the respective node.
This example assumes that you have Gatsby set up with Markdown and want to use the following frontmatter:
---title: My first blog post!featuredImgUrl: https://images.unsplash.com/photo-1560237731-890b122a9b6cfeaturedImgAlt: Mountains with a starry sky---
const { createRemoteFileNode } = require("gatsby-source-filesystem")
exports.createSchemaCustomization = ({ actions }) => { const { createTypes } = actions createTypes(` type MarkdownRemark implements Node { frontmatter: Frontmatter featuredImg: File @link(from: "fields.localFile") } type Frontmatter { title: String! featuredImgUrl: String featuredImgAlt: String } `)}
exports.onCreateNode = async ({ node, actions: { createNode, createNodeField }, store, cache, createNodeId,}) => { // For all MarkdownRemark nodes that have a featured image url, call createRemoteFileNode if ( node.internal.type === "MarkdownRemark" && node.frontmatter.featuredImgUrl !== null ) { const fileNode = await createRemoteFileNode({ url: node.frontmatter.featuredImgUrl, // string that points to the URL of the image parentNodeId: node.id, // id of the parent node of the fileNode you are going to create createNode, // helper function in gatsby-node to generate the node createNodeId, // helper function in gatsby-node to generate the node id cache, // Gatsby's cache store, // Gatsby's Redux store }) // if the file was created, extend the node with "localFile" if (fileNode) { createNodeField({ node, name: "localFile", value: fileNode.id }) } }}
#Analyse your Bundle Sizes
Don’t know exactly why some of your pages load slowly? No problem, you can use gatsby-plugin-webpack-bundle-analyser-v2 to find the culprits.
After installation and setup you’ll see a visual representation of all your bundles.
npm install gatsby-plugin-webpack-bundle-analyser-v2 -D
And in your gatsby-config.js
:
module.exports = { plugins: ["gatsby-plugin-webpack-bundle-analyser-v2"],}
Once you run a gatsby build
at the end of a build (locally) the browser will open with the bundle analyser.
#More tips!
You can read more posts about Gatsby in my Digital Garden or in the React writing category. Some highlights:
- Using Deferred Static Generation with Analytics Tools
- Filter Future Posts on a Gatsby Blog
- Adding Support for Multiple Authors in Gatsby
- Adding a Draft Feature to Gatsby
#Your tips?
Do you have any tips that I should add to this list? Let me know on Twitter at @lekoarts_de and I’ll add them!