Skip to content

Ultra-schnellen Blog mit React und Gatsby erstellen

04.02.201815 Min. LesezeitKategorie: Tutorial

Update vom 05.02.2019

Das Tutorial wurde komplett überarbeitet und nutzt nun die aktuelle Version (v2) von Gatsby und dessen default-starter!

Gatsby ist ein blitzschneller Static Site Generator basierend auf React. Wer keine Lust auf ein großes CMS wie Wordpress hat, aber dennoch gerne etwas mehr als reine HTML-Seiten bearbeiten will, wird Gatsby sicherlich lieben (natürlich muss man auch React mögen). Gatsby erfüllt alle Ansprüche an eine Progressive Web App (PWA), gibt per GraphQL Zugriff auf verschiedene Datenimporte und hat durch Code-Splitting und Service Worker extrem schnelle Ladezeiten. Durch den Einsatz eines Headless CMS wie zum Beispiel Contentful oder Netlify CMS kann man Wordpress auch komplett ersetzen.

Ziel dieses Tutorials ist es einen funktionierenden Blog mit Artikel-Übersicht und Markdown-Unterstützung zu erstellen. Am Ende wirst du eine einfach gehaltene (neudeutsch: minimal) Website innerhalb von circa 15 Minuten erstellt haben. Dem Tutorial begleitend gibt es ein öffentliches GitHub Repository.

Hinweis: Gatsby selber bietet eine hervorragende Dokumentation und Tutorials auf Englisch an. Um noch andere Möglichkeiten (z.B. anderes CSS) kennenzulernen solltest du unbedingt die Website besuchen. Um diesen und nachfolgenden Tutorials folgen zu können, solltest du bereits Grundkenntnisse in React haben.

Inhaltsverzeichnis

  1. Starten
  2. Konfiguration und Plugins
  3. Layout
  4. Blogposts mit Markdown erstellen
  5. Startseite mit Artikel-Übersicht füllen
  6. Export und Hochladen

#Starten

Installiere eine aktuelle Version von Node.js auf deinem Computer oder checke deine Version im Terminal mit node --version. Der Paketmanager npm wird dann gleich mitgeliefert.

Navigiere anschließend zu deinem gewünschten Ziel-Order und erstelle mit Hilfe der CLI ein neues Projekt ("gatsby-one" ist der Name des Ordners):

npx gatsby new gatsby-one

Am Ende solltest du diese Elemente in deinem Ordner haben:

#Konfiguration und Plugins

Gatsby ist bereits von Haus aus sehr gut konfigurierbar und mit Plugins erweiterbar. Um im späteren Verlauf u.a. Markdown nutzen zu können, musst du vorher erst diverse Plugins installieren und anpassen. Öffne hierzu wieder dein Terminal und installiere mit npm folgende Pakete:

npm install --save gatsby-plugin-catch-links gatsby-plugin-lodash gatsby-plugin-typography gatsby-remark-autolink-headers gatsby-remark-external-links gatsby-transformer-remark react-typography typography

Uff... Das sind viele Plugins. In der offiziellen Übersicht kannst du im Detail nachlesen, was diese bewirken - für uns sind die Details momentan noch nicht interessant.

Alle Plugins werden in der Datei gatsby-config.js aktiviert und konfiguriert.

module.exports = {
  siteMetadata: {
    title: `Minimal Blog`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}

Es wurde bereits automatisch ein Plugin in die Liste eingetragen, jetzt folgen die gerade installierten Plugins. Zuerst teilen wir Gatsby mit, wo es unsere Markdown-Dateien erwarten kann und geben diesem Verzeichnis einen einzigartigen Namen:

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `post`,
        path: `${__dirname}/blog`,
      },
    },
  ],
}

Im Root-Verzeichnis / erstellst du nun den Ordner blog mit einem Unterordner 2019-02-05. Hier drin dann eine Markdown-Datei mit ein bisschen Fülltext.

Markdown-Datei inklusive Frontmatter:

---
date: "2018-01-01"
title: "Scittle Luo"
---

Hier kommt dein Fülltext hin

Remark wird es später sein, das diese Dateien zu gültigem HTML verarbeitet. gatsby-transformer-remark nimmt Optionen und weitere Plugins entgegen.

    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-external-links`,
            options: {
              target: `_blank`,
              rel: `nofollow noopener noreferrer`
            }
          },
          `gatsby-remark-autolink-headers`
        ]
      }
    }

Um bequem Google Fonts nutzen zu können nehmen wir Typography.js.

    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography.js`,
      },
    },

Innerhalb des src Ordners erstellst du einen neuen Ordner namens utils und dort die Datei typography.js. Hiermit können wir Typography.js konfigurieren und z.B. unsere Google Fonts festlegen. Zuerst importieren wir das Paket, erstellen dann ein neues Objekt und übergeben diesem unsere Einstellungen:

src/utils/typography.js
import Typography from "typography"

const typography = new Typography({
  title: "Minimal",
  baseFontSize: "16px",
  baseLineHeight: 1.66,
  scaleRatio: 3.66,
  headerFontFamily: ["Bitter", "sans-serif"],
  bodyFontFamily: ["Open Sans", "sans-serif"],
  headerWeight: 700,
  googleFonts: [
    {
      name: "Bitter",
      styles: ["700"]
    },
    {
      name: "Open Sans",
      styles: ["400"]
    }
  ]
})

export default typography

Am Ende sieht unsere gatsby-config.js Datei wie folgt aus:

module.exports = {
  siteMetadata: {
    title: `Minimal Blog`,
    description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
    author: `@gatsbyjs`,
  },
  plugins: [
    `gatsby-plugin-react-helmet`,
    `gatsby-plugin-catch-links`,
    `gatsby-plugin-lodash`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `post`,
        path: `${__dirname}/blog`,
      },
    },
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-external-links`,
            options: {
              target: `_blank`,
              rel: `nofollow noopener noreferrer`
            }
          },
          `gatsby-remark-autolink-headers`
        ]
      }
    },
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography.js`,
      },
    },
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-plugin-manifest`,
      options: {
        name: `gatsby-starter-default`,
        short_name: `starter`,
        start_url: `/`,
        background_color: `#663399`,
        theme_color: `#663399`,
        display: `minimal-ui`,
        icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}

#Layout

Starte den lokalen Server:

npm run develop

Am Ende solltest du folgende Nachricht sehen:

You can now view gatsby-starter-default in the browser.

  http://localhost:8000/

View GraphiQL, an in-browser IDE, to explore your site's data and schema

  http://localhost:8000/___graphql

Note that the development build is not optimized.
To create a production build, use gatsby build

Nun haben wir einen lokalen Hot-Reloading Server, d.h. unsere Änderungen in den Dateien werden automatisch ohne manuelles Aktualisieren in der Live-Version zu sehen sein. Auch findest du unter ___graphql den GraphiQL Editor, in dem du GraphQL Queries ausprobieren kannst. Sehr zu empfehlen, um die passende Query zu finden!

Das Layout sollte immer der Startpunkt in deinem Projekt sein. Hier definierst du globale Einstellungen, z.B. kannst du hier CSS-Dateien oder Meta-Tags setzen, die auf jeder Unterseite eingebunden werden sollen. Öffne die layout.js Datei, die im Ordner src/components zu finden ist. Entferne die Header Komponente, die StaticQuery und das Styling am inneren Container. Außerdem kannst du den Footer anpassen. Unsere Datei sieht dann so aus:

src/components/layout.js
import React from 'react';
import PropTypes from 'prop-types';

import './layout.css';

const Layout = ({ children }) => (
  <>
    <main>{children}</main>
    <footer style={{ textAlign: 'center', padding: '3rem 0' }}>
      &copy; 2018 by John Doe. All rights reserved.
    </footer>
  </>
);

Layout.propTypes = {
  children: PropTypes.node.isRequired,
};

export default Layout;

Ganz oben importieren wir nur React und PropTypes, darunter unsere CSS Datei. children ist der Inhalt, der in unser Layout kommen wird - in diesem Fall sind es die Seiten im src/pages Ordner.

Gib dem <main> Tag einen Klassennamen. Achte darauf, dass du nicht class sondern className nutzen musst (da class ein reserviertes Wort in JS ist).

<main className="layout">{children}</main>

Öffne layout.css. Den Inhalt kannst du komplett löschen, einen CSS-Reset erledigt Typography.js schon für uns. Zuerst wollen wir ein paar Grundeinstellungen festlegen:

src/components/layout.css
::selection {
  color: white;
  background: #d02e77;
}

html {
  background: #fff;
}

a {
  color: #d02e77;
  text-decoration: none;
  transition: color 0.5s;
}

a:hover {
  color: #624464;
}

/* Tablet */

@media (max-width: 1200px) {
  html {
    font-size: 15px;
  }
}

/* Phone */
@media (max-width: 600px) {
  html {
    font-size: 14px;
  }
}

Da wir unseren Inhalt mit einer variablen Breite zentrieren wollen, bietet sich die neue CSS-Grid Syntax an. Natürlich wäre auch ein margin: 0 auto möglich. An dieser Stelle möchte ich den CSS-Grid Kurs von Wes Bos empfehlen, falls du tiefer in die Materie einsteigen willst.

.layout {
  display: grid;
  grid-template-columns: 1fr minmax(320px, 1000px) 1fr;
}

Hier definieren wir insgesamt drei Spalten, die jeweils äußeren nehmen 1fr Platz ein, wobei fr für Fraction steht. CSS-Grid nimmt immer erst absolute Werte (wie z.B. Pixel) und teilt danach die fr auf. Wir wollen unsere mittlere Spalte eine Größe von 320px bis 1000px geben, die Seite links und rechts wird dann jeweils aufgefüllt, falls noch Platz da seien sollte.

#Blogposts mit Markdown erstellen

Beende den laufenden Gatsby dev server (z.B. mit Strg + C) und erstelle einen neuen Ordner templates innerhalb des src Ordners. Erzeuge dort eine Datei namens post.js - diese ist das Template für die Artikel-Ansicht und wird zur automatischen Erstellung der Seiten gebraucht. Wir erstellen wieder eine Functional Stateless Component:

src/templates/post.js
import React from 'react';
import { Link } from 'gatsby';
import Layout from '../components/layout'
import styles from './post.module.css';

const Post = props => {
  return (
    <Layout>
      <article className={styles.blogPost}>
        <Link to="/">
          Gatsby Starter - Minimal Blog | Get back to the overview
        </Link>
        <div
          className={styles.content}
          dangerouslySetInnerHTML={{ __html: postNode.html }}
        />
      </article>
    <Layout>
  );
};

export default Post;
src/templates/post.module.css
.blogPost {
  grid-column: 2;
  box-shadow: 0 0 120px rgba(0, 0, 0, 0.1);
  border-radius: 0 0 1rem 1rem;
  padding: 2rem 6rem;
}

.title {
  margin-top: 2rem;
}

.content {
  margin-top: 4rem;
}

aside {
  grid-column: 3;
}

/* Tablet */

@media (max-width: 1200px) {
  .blogPost {
    padding: 3rem 4rem;
  }
}

/* Phone */
@media (max-width: 600px) {
  .blogPost {
    padding: 2rem 1.5rem;
  }
}

Dieses mal importieren wir unser CSS etwas anders, nämlich indem wir das schon eingebaute CSS-Modules nutzen (.module muss vor dem .css stehen). Webpack gibt jeder Klasse einen einzigartigen Namen, sodass theoretisch jede React Komponenten die Klasse .content haben kann, aber es durch die verschiedenen Namen keine Überschneidungen gibt.

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default.

Die Voraussetzungen, dass postNode.html überhaupt Inhalt hat, schaffen wir jetzt.

Eine SEO-freundliche URL soll für alle Artikel automatisch erstellt werden, weshalb wir im ersten Schritt mit Hilfe von lodash ein sog. NodeField für Gatsby erstellen. Dieses können wir in der späteren GraphQL Query dann abfragen.

gatsby-node.js
const _ = require('lodash');

exports.onCreateNode = ({ node, actions }) => {
  const { createNodeField } = actions;
  let slug;
  if (node.internal.type === 'MarkdownRemark') {
    if (
      Object.prototype.hasOwnProperty.call(node, 'frontmatter') &&
      Object.prototype.hasOwnProperty.call(node.frontmatter, 'slug')
    ) {
      slug = `/${_.kebabCase(node.frontmatter.slug)}`;
    }
    if (
      Object.prototype.hasOwnProperty.call(node, 'frontmatter') &&
      Object.prototype.hasOwnProperty.call(node.frontmatter, 'title')
    ) {
      slug = `/${_.kebabCase(node.frontmatter.title)}`;
    }
    createNodeField({ node, name: 'slug', value: slug });
  }
};

In den Markdown-Dateien definieren wir in der Frontmatter unseren Titel oder Slug (falls wir eine bestimmte URL haben wollen), der hier extrahiert, mit lodash in ein passendes Format gebracht und am Ende als NodeField definiert wird.

Gatsby bietet unter anderem auch eine API an, um automatisch Seiten (außerhalb von src/pages) mit Hilfe eines Templates zu erstellen. Zusammen mit der createPages Funktion sieht unsere gatsby-node.js wie folgt aus:

gatsby-node.js
const _ = require('lodash');

exports.onCreateNode = ({ node, boundActionCreators }) => {
  const { createNodeField } = boundActionCreators;
  let slug;
  if (node.internal.type === 'MarkdownRemark') {
    if (
      Object.prototype.hasOwnProperty.call(node, 'frontmatter') &&
      Object.prototype.hasOwnProperty.call(node.frontmatter, 'slug')
    ) {
      slug = `/${_.kebabCase(node.frontmatter.slug)}`;
    }
    if (
      Object.prototype.hasOwnProperty.call(node, 'frontmatter') &&
      Object.prototype.hasOwnProperty.call(node.frontmatter, 'title')
    ) {
      slug = `/${_.kebabCase(node.frontmatter.title)}`;
    }
    createNodeField({ node, name: 'slug', value: slug });
  }
};

// graphql function returns a promise so we can use this little promise helper to have a nice result/error state
const wrapper = promise =>
  promise
    .then(result => ({ result, error: null }))
    .catch(error => ({ error, result: null }));

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const postPage = require.resolve('./src/templates/post.js');

  const { error, result } = await wrapper(
    graphql(`
      {
        posts: allMarkdownRemark {
          edges {
            node {
              fields {
                slug
              }
            }
          }
        }
      }
    `)
  );

  if (!error) {
    const posts = result.data.posts.edges;

    posts.forEach(edge => {
      createPage({
        path: edge.node.fields.slug,
        component: postPage,
        context: {
          slug: edge.node.fields.slug,
        },
      });
    });

    return
  }

  console.log(error)
};

postPage ist unsere Template-Komponente. In der GraphQL Query geben wir der Query den Alias posts und fragen den weiter oben definierten slug ab. Sollten dabei keine Fehler auftreten speichern wir das Ergebnis dieser Abfrage in der Variable posts. In der createPage Funktion übergeben wir drei Werte:

  • path: Die schlussendliche URL
  • component: Definieren der Komponente
  • context: Es wird sozusagen der Wert weitergegeben (bieten einen "Kontext") und können somit diesen Wert als Variable in GraphQL Queries nutzen

Zurück in unserer post.js Datei erstellen wir die passende GraphQL Query:

src/templates/post.js
export const postQuery = graphql`
  query postBySlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date(formatString: "DD.MM.YYYY")
      }
    }
  }
`;

Was passiert hier? Wir geben der Variable und Query einen Namen (der Name ist egal, er muss nur einzigartig sein) und teilen GraphQL mit, dass wir slug als Variable definieren, die ein String seien muss. Dieses $slug ist unser slug aus gatsby-node.js. Um nur eine Datei zurückzubekommen, muss die Variable und das interne Feld identisch sein - wir bekommen also nur jeweils die Daten zur passenden Datei zurück. html ist der gesamte Text, beim Datum können wir noch eine spezielle Formatierung angeben.

Nun wollen wir die Ergebnisse dieser Query in unserem Template einbauen. Öffne post.js und füge diese zwei Zeilen hinzu:

const Post = props => {
  const postNode = props.data.markdownRemark;
  const post = postNode.frontmatter;

An die Daten der Query kommt man mit props.data.markdownRemark. Tipp: Falls du zwei markdownRemark Queries nutzen willst, kannst du wieder Aliase nutzen. Im Falle von post: markdownRemark würde man dann props.data.post nutzen. Das Template sollte dann so aussehen:

src/templates/post.js
import React from 'react';
import { Link } from 'gatsby';
import styles from './post.module.css';

const Post = props => {
  const postNode = props.data.markdownRemark;
  const post = postNode.frontmatter;

  return (
    <Layout>
      <article className={styles.blogPost}>
        <Link to="/">
          Gatsby Starter - Minimal Blog | Get back to the overview
        </Link>
        <div
          className={styles.content}
          dangerouslySetInnerHTML={{ __html: postNode.html }}
        />
      </article>
    <Layout>
  );
};

export default Post;

export const postQuery = graphql`
  query postBySlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date(formatString: "DD.MM.YYYY")
      }
    }
  }
`;

Nutze nun die post Variable, um mehr Informationen zum Blogpost hinzuzufügen. Auch wollen wir dem Blogpost einen eigenen Titel (Meta-Tags) geben, weshalb wir Gatsby's SEO Komponente nutzen. Am Ende sieht es dann so aus:

src/templates/post.js
import React from 'react';
import { Link, graphql } from 'gatsby';
import Layout from '../components/layout'
import styles from './post.module.css';
import SEO from '../components/seo';

const Post = props => {
  const postNode = props.data.markdownRemark;
  const post = postNode.frontmatter;

  return (
    <Layout>
      <SEO title={`${post.title}`} />
      <article className={styles.blogPost}>
        <Link to="/">
          Gatsby Starter - Minimal Blog | Get back to the overview
        </Link>
        <h1 className={styles.title}>{post.title}</h1>
        <h4 className={styles.date}>{post.date}</h4>
        <div
          className={styles.content}
          dangerouslySetInnerHTML={{ __html: postNode.html }}
        />
      </article>
    </Layout>
  );
};

export default Post;

export const postQuery = graphql`
  query postBySlug($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        date(formatString: "DD.MM.YYYY")
      }
    }
  }
`;

Starte mit npm run develop wieder den Server. Die Startseite sieht noch genauso aus wie vorher, aber rufe mal http://localhost:8000/scittle-luo auf. Tadaaaa. Der Inhalt unserer Datei in unserem Template!

#Startseite mit Artikel-Übersicht füllen

Bislang grüßt dich ein Hi people beim Aufruf der Startseite... Hier sollen unsere Artikel aufgelistet und darüber eine kleine Nachricht angezeigt werden. Beende den lokalen Server wieder und erweitere src/pages/index.js um diese Query:

src/pages/index.js
export const IndexQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
            date(formatString: "DD.MM.YYYY")
          }
          excerpt(pruneLength: 200)
        }
      }
    }
  }
`;

Wir sortieren die Liste absteigend nach Datum, holen uns Infos wie Pfad, Titel, Datum und einen Ausschnitt (HTML wird auf 200 Zeichen gekürzt ausgegeben).

Außerdem erstellen wir wieder eine CSS-Datei und das generelle Markup. Am Ende sollte die Datei so aussehen:

src/pages/index.js
import React from 'react';
import { graphql } from 'gatsby';

import Layout from '../components/layout';
import SEO from '../components/seo';
import styles from './index.module.css';

const IndexPage = ({ data }) => (
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
    <div className={styles.indexContent}>
      <div className={styles.hero}>
        <h1>Hi.</h1>
        <p>
          I&apos;m John Doe, a Senior UX Developer with five years of industry
          experience, specializing in developing React apps with the best UX
          users can get.
        </p>
      </div>
      {/* Hier kommt nun unsere Übersicht hin */}
    </div>
  </Layout>
);

export default IndexPage;

export const IndexQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
            date(formatString: "DD.MM.YYYY")
          }
          excerpt(pruneLength: 200)
        }
      }
    }
  }
`;
src/pages/index.module.css
.indexContent {
  grid-column: 2;
  box-shadow: 0 0 120px rgba(0, 0, 0, 0.1);
  border-radius: 0 0 1rem 1rem;
  padding: 3rem 6rem;
}

.hero p {
  font-size: 1.68rem;
  margin-top: -1rem;
}

.hero {
  margin-bottom: 12rem;
  text-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
  color: #000;
}

/* Tablet */

@media (max-width: 1200px) {
  .hero p {
    font-size: 1.45rem;
  }
  .indexContent {
    padding: 3rem 3rem;
  }
}

/* Phone */
@media (max-width: 600px) {
  .hero p {
    font-size: 1.25rem;
  }
  .indexContent {
    padding: 2rem 1.5rem;
  }
}

React lebt von der Abstrahierung in Komponenten, weshalb wir für die Übersicht eine Artikel Komponente erstellen. Gehe dazu in den src/components Ordner und erstelle eine article.js und article.module.css Datei:

src/components/article.js
import React from 'react';
import PropTypes from 'prop-types'
import { Link } from 'gatsby';

import styles from './article.module.css';

const Article = ({ title, date, excerpt, slug }) => {
  const firstChar = title.charAt(0);

  return (
    <article className={styles.post}>
      <h2 className={styles.title}>
        <span className={styles.initiale}>{firstChar}</span>
        <Link to={slug}>{title}</Link>
      </h2>
      <h4 className={styles.date}>{date}</h4>
      <p className={styles.excerpt}>{excerpt}</p>
    </article>
  );
};

export default Article;

Article.propTypes = {
  title: PropTypes.string.isRequired,
  date: PropTypes.string.isRequired,
  excerpt: PropTypes.string.isRequired,
  slug: PropTypes.string.isRequired,
}

Statt props nutzen wir dieses mal wieder direkt das Destructuring und greifen uns sofort die Informationen ab (andere Schreibweise für props.title etc.). In firstChar speichern wir den ersten Buchstaben des Titels. Die CSS Datei sieht wie folgt aus:

src/components/article.module.css
.post {
  display: grid;
  grid-template-columns: 3fr 1fr;
  align-items: center;
  margin-bottom: 4rem;
}

.initiale {
  position: absolute;
  font-size: 8rem;
  transform: translate(-50%, -50%);
  opacity: 0.05;
  user-select: none;
  z-index: -1;
}

.title {
  position: relative;
  margin-bottom: 0;
  text-shadow: 0 12px 30px rgba(0, 0, 0, 0.15);
}

.title a {
  color: hsla(0, 0%, 0%, 0.8);
}

.title a:hover {
  color: #d02e77;
}

.date {
  margin-bottom: 0;
  text-align: right;
}

.excerpt {
  grid-column: -1 / 1;
  margin-top: 1.35rem;
  margin-bottom: 0;
}

Wieder zurück in pages/index.js importieren wir die neue Komponente und nutzen sie, um unsere Liste zu erstellen:

src/pages/index.js
import React from 'react';
import { graphql } from 'gatsby';

import Layout from '../components/layout';
import SEO from '../components/seo';
import Article from '../components/article';
import styles from './index.module.css';

const IndexPage = ({ data }) => (
  <Layout>
    <SEO title="Home" keywords={[`gatsby`, `application`, `react`]} />
    <div className={styles.indexContent}>
      <div className={styles.hero}>
        <h1>Hi.</h1>
        <p>
          I&apos;m John Doe, a Senior UX Developer with five years of industry
          experience, specializing in developing React apps with the best UX
          users can get.
        </p>
      </div>
      {data.allMarkdownRemark.edges.map(post => (
        <Article
          title={post.node.frontmatter.title}
          date={post.node.frontmatter.date}
          excerpt={post.node.excerpt}
          slug={post.node.fields.slug}
          key={post.node.fields.slug}
        />
      ))}
    </div>
  </Layout>
);

export default IndexPage;

export const IndexQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) {
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
            date(formatString: "DD.MM.YYYY")
          }
          excerpt(pruneLength: 200)
        }
      }
    }
  }
`;

Über das data.allMarkdownRemark.edges Array wird komplett einmal iteriert, die einzelnen Einträge kann man mit post beziehen. Unsere Properties an der Komponente füllen wir dann mit eben diesen Einträgen. Das key Property muss für React gesetzt werden.

Abschließend kannst du noch die Datei src/pages/page-2.js löschen, da diese nicht gebraucht wird.

Et voilà. Starte den Server neu und betrachte die schöne Startseite :)

#Export und Hochladen

Die lokale Version läuft und zum jetzt wollen wir natürlich dem Internet unseren schönen Blog präsentieren. Beende den Server und führe npm run build aus. Gatsby nimmt nun alle Dateien/Queries, minimiert diese und erstellt die allgemeine Struktur. Den Inhalt des Ordners public kannst du per FTP auf deinen Host hochladen.

Aber...

Das ist auf Dauer ziemlich umständlich. Immerhin muss bei jeder Änderung oder bei jedem neuen Artikel die Seite neu erstellt und hochgeladen werden. Der einfachste Weg dies zu automatisieren ist Netlify.

Nachdem du einen kostenlosen Account erstellt hast, siehst du diese Übersicht:

Mit dem Button New site from Git kannst du direkt dein GitHub/GitLab/BitBucket Repository auswählen (wenn du kein Git benutzt, solltest du das schleunigst ändern :P). Netlify erkennt automatisch, dass der Befehl gatsby build lautet und der public/ Order ausgegeben werden soll. Per Deploy site Button ist die Seite ganz schnell live.

Interesse geweckt? Lies alle Beiträge in der Kategorie Tutorial

Weitere Beiträge