Skip to content

Tipps & Tricks für Gatsby

02.03.20199 Min. LesezeitKategorie: Tutorial

Seit längerer Zeit versuche ich immer mal wieder einen Quicktipp zu Gatsby auf Twitter zu veröffentlichen, da die Community solche kurzen, nützlichen Tipps liebt. Dementsprechend hatte ich schon damals einen Twitter Moment erstellt – aber wieso eigentlich nur Content von mir? Am 6. Januar diesen Jahres hatte ich auch dann auch die Community aufgerufen ihre Quicktipps zu teilen (mit großem Erfolg). Wie versprochen soll dieser Blogpost eine Sammlung dieser (und weiterer) Tipps darstellen. Gerne kannst du mich auch auf Twitter markieren, falls dein Tipp zum Moment hinzugefügt werden soll!

Einige der Tipps werden darüber hinaus von Codesandbox Beispielen begleitet.

Inhalte der Tweets

Die meisten Tipps sind Code-Beispiele, die im Original in einem Bild eingebettet sind. Für einen Blogpost würde es wenig Sinn machen Bilder statt kopierbaren Code anzuzeigen – daher werden die Tweets hier nicht eingebettet, sondern die Autoren anders zitiert. Solltest du Autor einer dieser Tweets und nicht damit einverstanden sein bitte ich dich bei mir zu melden. Danke!

#Datum der letzten Veröffentlichung

Gibt das Datum an, an dem das letzte Mal gatsby build ausgeführt wurde. Im Falle von z.B. Netlify ist das der Zeitpunkt des letzten Deploys.
Codesandbox

import React from 'react'
import { useStaticQuery, graphql } from 'gatsby'

const IndexPage = () => {
  const data = useStaticQuery(query)

  return (
    <>
      <p>This site was last built on:</p>
      <p>{data.site.buildTime}</p>
    </>
  )
}

export default IndexPage

const query = graphql`
  query Info {
    site {
      buildTime(formatString: "DD/MM/YYYY")
    }
  }
`

#Datum der letzten Änderung

Für Dokumentation ist es hilfreich zu wissen wann ein Dokument zuletzt bearbeitet wurde. Diese Information kannst du für Dateien über das parent Attribut bekommen. Nutze den ___graphql Endpunkt, um diese Information in deinem eigenen Projekt zu finden.
Codesandbox

query YourQuery {
  allMdx {
    edges {
      node {
        parent {
          ... on File {
            modifiedTime(formatString: "MM/DD/YYYY")
          }
        }
      }
    }
  }
}

#Gleiche Datenquelle, verschiedene Queries

Wenn du beispielsweise zwei gatsby-source-filesystem definierst, kannst du deine GraphQL Queries filtern, um nur eine dieser Quellen zu erhalten.
Codesandbox

gatsby-config.js
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`,
  ],
}

Platziere nun eine Bilddatei in den Ordner src/logos und zwei oder mehr Bilddateien in den Ordner src/assets. Dies soll den Unterschied zwischen der file und allFile Query zeigen (es können natürlich beliebig viele Dateien in einen Ordner). Die beiden Ordner kannst du individuell ansprechen, wie im folgenden Beispiel zu sehen:

src/pages/index.js
import React from 'react'
import { useStaticQuery, graphql } from 'gatsby'
import Img from 'gatsby-image'

const IndexPage = () => {
  const data = useStaticQuery(query)

  return (
    <>
      <p>First image (logo):</p>
      <Img
        style={{ width: '200px', marginBottom: '2rem' }}
        fluid={data.logo.childImageSharp.fluid}
      />
      <p>Assets images (two):</p>
      <div style={{ display: 'flex', flexWrap: 'wrap' }}>
        {data.assets.edges.map(img => (
          <Img
            style={{ width: '200px', marginBottom: '2rem' }}
            fluid={img.node.childImageSharp.fluid}
          />
        ))}
      </div>
    </>
  )
}

export default IndexPage

const query = graphql`
  query Images {
    logo: file(sourceInstanceName: { eq: "logos" }) {
      childImageSharp {
        fluid(maxWidth: 200, quality: 100) {
          ...GatsbyImageSharpFluid
        }
      }
    }
    assets: allFile(filter: { sourceInstanceName: { eq: "assets" } }) {
      edges {
        node {
          childImageSharp {
            fluid(maxWidth: 200, quality: 100) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }
`

#Gatsby's reporter API

Du kannst nicht nur async/await in gatsby-node.js nutzen, sondern auch Gatsby's reporter API nutzen. Mit dieser kannst du Fehlermeldungen erstellen, falls mal was schiefgehen sollte. Mit panicOnBuild schlägt der Prozess nur bei gatsby build fehl (damit du GraphiQL zum Debuggen nutzen kannst).

exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions
  const yourTemplate = require.resolve('./src/templates/yourTemplate.js')

  const result = await graphql(`
    {
      ... your GraphQL query
    }
  `)

  if (result.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }
  
  result.data.yourNode.edges.forEach(edge => {
      // createPage function
  })
}

#Vorheriger/Nächster - Verlinkung

Gerne verlinkt man am Ende eines Blog Eintrags den vorherigen/nächsten Post, um den Leser auf der Seite zu halten. Hierfür kannst du pageContext nutzen, um Informationen weiterzuleiten.
Codesandbox

gatsby-node.js
const wrapper = promise =>
  promise.then(result => {
    if (result.errors) {
      throw result.errors
    }
    return result
  })

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const bookTemplate = require.resolve('./src/templates/book.js')

  const result = await wrapper(
    graphql(`
      {
        allBooksYaml(sort: { fields: [title], order: ASC }) {
          edges {
            node {
              slug
              title
            }
          }
        }
      }
    `)
  )

  const books = result.data.allBooksYaml.edges

  books.forEach((edge, 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].node
    const next = index === books.length - 1 ? null : books[index + 1].node

    createPage({
      path: edge.node.slug,
      component: bookTemplate,
      context: {
        slug: edge.node.slug,
        prev,
        next,
      },
    })
  })
}
src/templates/book.js
import React from 'react'
import { graphql, Link } from 'gatsby'

import Layout from '../components/layout'

class BookTemplate extends React.Component {
  render() {
    const { booksYaml } = this.props.data
    // prev & next passed through from gatsby-node.js
    const { prev, next } = this.props.pageContext
    return (
      <Layout location={this.props.location}>
        <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
    }
  }
`

#Zufällige Verlinkung

Wenn es nicht der vorherige/nächste Beitrag seien soll, sondern zwei (oder beliebig viele) weitere, ist das auch in gatsby-node.js möglich.
Codesandbox

const _ = require('lodash')

const prevNext = (list, item) => {
  // Create a random selection of other posts (excluding the current post)
  const filterUnique = _.filter(
    list,
    input => input.node.slug !== item.node.slug
  )
  const sample = _.sampleSize(filterUnique, 2)

  return {
    left: sample[0].node,
    right: sample[1].node,
  }
}

const wrapper = promise =>
  promise.then(result => {
    if (result.errors) {
      throw result.errors
    }
    return result
  })

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions
  const bookTemplate = require.resolve('./src/templates/book.js')

  const result = await wrapper(
    graphql(`
      {
        allBooksYaml {
          edges {
            node {
              slug
              title
            }
          }
        }
      }
    `)
  )

  const books = result.data.allBooksYaml.edges

  books.forEach(edge => {
    const { left, right } = prevNext(books, edge)

    createPage({
      path: edge.node.slug,
      component: bookTemplate,
      context: {
        slug: edge.node.slug,
        left,
        right,
      },
    })
  })
}

#TailwindCSS mit CSS-in-JS Bibliotheken nutzen

Tailwind ist kein UI-Kit, sondern ein "utility-first" CSS Framework. Mithilfe eines Babel Macros lässt es sich unkompliziert mit jeder CSS-in-JS Bibliothek nutzen (da Gatsby Babel Macros unterstützt).
Codesandbox

// Dieser Guide nimmt an, dass du bereits eine CSS-in-JS Bibliothek installiert hast
// In diesem Beispiel nehmen wir "styled-components"

// Schritt 1: Installiere die npm Pakete

$ npm install --save tailwind.macro tailwindcss

// Schritt 2: Erstelle eine Tailwind Config Datei

$ ./node_modules/.bin/tailwind init tailwind.js

// Schritt 3: Nutze das Babel Macro

import styled from 'styled-components'
import tw from 'tailwind.macro'

const Button = styled.button`
  ${tw`bg-blue hover:bg-blue-dark text-white font-bold py-2 px-4 rounded font-sans border-none`}
`

#Quicktipps

Hier mal ein paar kürzere Tipps, um die Auflistung aufzulockern :)

  • Nutze Cypress zum Testen deiner Website (Beispiel: lekoarts.de) - Quelle
  • Erstelle einen Helfer, der if typeof window !== 'undefined' checkt und nutze ihn wo immer du Browser APIs nutzt - Quelle
  • Optimiere deine Bilder vorher! Gatsby verarbeitet Bilder schneller, wenn du vorher schon deine Bilder optimierst, z.B. mit der Bildgröße. Wenn du bspw. schon von Anfang an weißt, dass deine Bilder nicht größer als 2000px seien sollen: Verkleinere alle Bilder auf diese Größe.
  • Wenn du Fragen hast: Besuche den Discord-Server von Gatsby! :) Andere Community Mitglieder helfen dir gerne
  • Alle .js/.jsx Dateien innerhalb des src/pages Ordners werden von Gatsby automatisch in Seiten umgewandelt. Am besten platzierst du jene Dateien woanders - oder wenn es gar nicht anders geht, kannst du diese z.B. mit einem Unterstrich versehen: _styled.js

#Fontawesome SSR

Du kannst Fontawesome Icons für das Server-Side Rendering fit machen!
Original Tweet

// Wherever you're importing your styles, include:
import '@fortawesome/fontawesome-svg-core/styles.css'

// or if you're using something like styled-components
import { createGlobalStyle } from 'styled-components'
import { dom } from '@fortawesome/fontawesome-svg-core'

const GlobalStyle = createGlobalStyle`
  // global site styles

  ${dom.css()}
`

#Downloade Bilder von einem CDN

Falls deine Bilder auf einem CDN gehosted werden und du gatsby-image nutzen willst, kannst du die Bilder mit dem createRemoteFileNode Helfer herunterladen. Anschließend fügst du das Bild zum jeweiligen Node hinzu.
Original Tweet

gatsby-node.js
const { createRemoteFileNode } = require('gatsby-source-filesystem')

exports.onCreateNode = async ({
  node,
  actions,
  store,
  cache,
  createNodeId,
}) => {
  const { createNodeField, createNode } = actions

  if (node.internal.type === 'YourNodeType') {
    const imageNode = await createRemoteFileNode({
      url: node.yourImage.href,
      store,
      cache,
      createNode,
      createNodeId,
    })

    if (imageNode) {
      createNodeField({
        node,
        name: `yourNewImage___NODE`,
        value: imageNode.id,
      })
    }
  }
}

#Auf dem Handy entwickeln

Du kannst deine lokale Website auch auf dem Handy testen!
Original Tweet

  1. gatsby develop -o -H $HOSTNAME -p 8000
  2. Besuche jetzt $IP_DEINES_PCs:8000

#Gatsby + Apollo

Willst du wissen, wie du build-time und runtime GraphQL nutzen kannst mit Apollo?
Original Tweet

gatsby-config.js
module.exports = {
  siteMetadata: {
    title: 'Gatsby With Apollo',
  },
  plugins: [
    {
      resolve: 'gatsby-source-graphql',
      options: {
        typeName: 'RMAPI',
        fieldName: 'rickAndMorty',
        url: 'https://rickandmortyapi-gql.now.sh/',
      },
    },
  ],
}
gatsby-browser.js & gatsby-ssr.js
import React from 'react'
import { ApolloProvider } from 'react-apollo'
import { client } from './src/apollo/client'

export const wrapRootElement = ({ element }) => (
  <ApolloProvider client={client}>{element}</ApolloProvider>
)
src/apollo/client.js
import ApolloClient from 'apollo-boost'
import fetch from 'isomorphic-fetch'

export const client = new ApolloClient({
  uri: 'https://nx9zvp49q7.lp.gql.zone/graphql',
  fetch,
})
src/pages/index.js
import React from 'react'
import { graphql } from 'gatsby'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'

// This query is executed at build time by Gatsby.
export const GatsbyQuery = graphql`
  {
    rickAndMorty {
      character(id: 1) {
        name
        image
      }
    }
  }
`

// This query is executed at run time by Apollo.
const APOLLO_QUERY = gql`
  {
    dog(breed: "frise") {
      breed
      displayImage
    }
  }
`

export default ({
  data: {
    rickAndMorty: { character },
  },
}) => (
  <div style={{ textAlign: 'center', width: '600px', margin: '50px auto' }}>
    <h1>{character.name} With His Pupper</h1>
    <p>
      Rick & Morty API data loads at build time. Dog API data loads at run time.
    </p>
    <div>
      <img src={character.image} alt={character.name} style={{ width: 300 }} />

      <Query query={APOLLO_QUERY}>
        {({ data, loading, error }) => {
          if (loading) return <p>Loading pupper...</p>
          if (error) return <p>Error: ${error.message}</p>

          const { displayImage: src, breed } = data.dog
          return <img src={src} alt={breed} style={{ maxWidth: 300 }} />
        }}
      </Query>
    </div>
  </div>
)

#Gatsby ohne GraphQL nutzen

Wusstest du, dass du Gatsby auch ohne GraphQL nutzen kannst? Das folgende Beispiel lädt Daten von der PokéAPI, erstellt eine Seite mit Links zu allen Pokémons, erstellt individuelle Pokémon Seiten und Seiten für die Fähigkeiten des jeweiligen Pokémons.
Original Tweet / Codesandbox

gatsby-node.js
const axios = require('axios')

const get = endpoint => axios.get(`https://pokeapi.co/api/v2${endpoint}`)

const getPokemonData = names =>
  Promise.all(
    names.map(async name => {
      const { data: pokemon } = await get(`/pokemon/${name}`)
      const abilities = await Promise.all(
        pokemon.abilities.map(async ({ ability: { name: abilityName } }) => {
          const { data: ability } = await get(`/ability/${abilityName}`)

          return ability
        })
      )

      return { ...pokemon, abilities }
    })
  )

exports.createPages = async ({ actions: { createPage } }) => {
  const allPokemon = await getPokemonData(['pikachu', 'charizard', 'squirtle'])

  // Create a page that lists all Pokémon.
  createPage({
    path: `/`,
    component: require.resolve('./src/templates/all-pokemon.js'),
    context: { allPokemon },
  })

  // Create a page for each Pokémon.
  allPokemon.forEach(pokemon => {
    createPage({
      path: `/pokemon/${pokemon.name}/`,
      component: require.resolve('./src/templates/pokemon.js'),
      context: { pokemon },
    })

    // Create a page for each ability of the current Pokémon.
    pokemon.abilities.forEach(ability => {
      createPage({
        path: `/pokemon/${pokemon.name}/ability/${ability.name}/`,
        component: require.resolve('./src/templates/ability.js'),
        context: { pokemon, ability },
      })
    })
  })
}

#Browser-Fenster automatisch öffnen

Wusstest du, dass Gatsby's CLI auch Optionen hat? Ich füge gerne folgendes Skript zu der scripts Sektion hinzu, um automatisch den Browser zu öffnen:

"scripts": {
  "dev": "gatsby develop -o"
}

#Eigene Hook mit useStaticQuery erstellen

React Hooks sind wohl das Lieblingsthema aller React Entwickler momentan... Passend dazu gibt es ja auch die useStaticQuery Hook. Nutze doch diese, um deine eigene Hook für die Metadaten zu bauen!
codesandbox

src/components/buildTime.js
import { useStaticQuery, graphql } from 'gatsby'

function useBuildTime() {
  const data = useStaticQuery(graphql`
    query Info {
      site {
        buildTime(formatString: "DD/MM/YYYY")
      }
    }
  `)

  return data.site.buildTime
}

export default useBuildTime
src/pages/index.js
import React from 'react'

import useBuildTime from '../components/buildTime'

const IndexPage = () => {
  const time = useBuildTime()

  return (
    <>
      <h1>
        Hooks{' '}
        <span aria-label="hook-emoji" role="img"></span>
      </h1>
      <p>Last build time was: {time}</p>
    </>
  )
}

export default IndexPage

#Nutze dotenv

Standardmäßig ist dotenv automatisch installiert mit Gatsby und erlaubt es dir ENV Variablen in Dateien abzulegen und diese über verschiedene Betriebssysteme zu nutzen. Importiere dotenv in deiner gatsby-config.js Datei und du kannst .env.development oder .env Dateien nutzen.

require('dotenv').config({
  path: `.env.${process.env.NODE_ENV}`,
})

#GraphQL Playground

Durch das Setzen einer ENV Variable kannst du Prisma Playground nutzen! Das geht beispielsweise per Skript in package.json:

"develop": "GATSBY_GRAPHQL_IDE=playground gatsby develop",

#Hast du weitere Tipps?

Vielen Dank fürs Lesen und ich hoffe, dass dir die Tipps gefallen haben! Teile den Beitrag gerne mit deinen Freunden oder schreib mir auf Twitter/per Mail, wenn du noch Fragen oder gar weitere Tipps hast! Wenn du einige der Tipps in Action sehen willst, kannst du mein GitHub Profil besuchen und meine Gatsby Starter ansehen :)

Markiert mit
Alle Tags

Lust auf mehr? Lies alle Beiträge in der Kategorie Tutorial

Weitere Beiträge