コンテンツにスキップするには Enter キーを押してください

Netlify CMS + Gatsbyを使ったJAMstackなブログを考えてみる

現在の社内ブログがwordpressでの運用に疲弊しているのと、Web技術のトレンドともなっているJAMstackが個人的に気になっていたので、検討してみました。

JAMstackが何かについてはここでは説明しません。
説明については
JAMstackサイト
JAMstackサイトの日本語訳Qiita記事
があるのでこちらが参考になります。

10月29、30日にはnetlifyがオーガナイザーを務めるJAMstack_conf がサンフランシスコで開催されます。
そのnetlifyが開発を進めているオープンソースNetlify CMS で簡単にJAMstackな構成でブログを作成することができました。

Gatsby Site Starterから始める

Start with a Template にアクセスしてみると、現在、静的サイトジェネレータ(SSG)のテンプレートが3つ選べます。

私はReactを書きたいのでGatsbyを選択しました。
あとはポチポチ押していくと1分足らずでGithubにPushされ、NetlifyにDeployされてブログが出来上がります。

/adminにアクセスすると管理側でのブログ投稿ができます。
管理側の認証はNetlify Identityが使われています。
記事がMarkdownで書けるのはうれしいですね。

アーキテクチャ

アーキテクチャは意外と単純で、
– 管理者認証にNetlify Identity (Git Gateway)
– 記事保存時に内容をMarkdownに変換しGithubへPush
– PushをトリガーにNetlifyが静的ページにBuild & Deployする

ブログはすでにコンテンツがレンダリングされた状態の静的ページをCDN経由で読み込むので、パフォーマンスが非常に良くなるということです。

[Gatsby] ブログ一覧ページをカスタマイズする

テンプレートにはAboutやProductページがあるので、関係するJSファイルはブログには必要ないので削除します。
とりあえず一覧ページをカスタマイズしてみます。
すでにCSS frameworkのBulmaが入っているのでそれを利用します。
まず先ほど作成したレポジトリからローカルにCloneします。
その後、

$ npm install
$ gatsby develop

すると開発サーバーが立ち上がりローカルで確認できます。

一覧ページの編集は src/pages/index.js で行います。
ソースコードをみるとJSXのコードとGraphQLでクエリを投げてデータを取得してきているがわかります。
これはMarkdownとして保存されている記事データを取得して表示しています。
どのようなクエリやデータが取れるかはGraphiQLツールを使って確認できます。
デフォルトでは localhost:8000/___graphql へアクセスすると使えます。

以下のように変更してみました。

import React from "react";
import PropTypes from "prop-types";
import { Link, graphql } from "gatsby";
import Layout from "../components/Layout";

export default class IndexPage extends React.Component {
  render() {
    const { data } = this.props;
    const { edges: posts } = data.allMarkdownRemark;

    return (
      <Layout>
        <section className="section">
          <div className="container">
            <div className="content">
              <h1 className="has-text-weight-bold is-size-2">Latest Stories</h1>
            </div>
            <div className="columns is-desktop is-multiline">
              {posts
                .map(({ node: post }) => (
                  <div className="column is-one-third" key={post.id}>
                    <div className="card">
                      <div className="card-image">
                        <figure className="image">
                          <img src={post.frontmatter.image} alt="post"/>
                        </figure>
                      </div>
                      <div className="card-content">
                        <div className="media">
                          <div className="media-content">
                            <p className="title is-4">
                              <Link className="has-text-primary" to={post.fields.slug}>
                                {post.frontmatter.title}
                              </Link>
                            </p>
                            <p className="subtitle is-6">Author: {post.frontmatter.author}</p>
                          </div>
                        </div>

                        <div className="content">
                          {post.excerpt}
                          <br/>
                          <Link className="button is-small" to={post.fields.slug}>
                            Keep Reading
                          </Link>
                          <br/>
                          <time>{post.frontmatter.date}</time>
                        </div>
                      </div>
                    </div>
                  </div>

                ))}
            </div>
          </div>
        </section>
      </Layout>
    );
  }
}

IndexPage.propTypes = {
  data: PropTypes.shape({
    allMarkdownRemark: PropTypes.shape({
      edges: PropTypes.array
    })
  })
};

export const pageQuery = graphql`
  query IndexQuery {
    allMarkdownRemark(
      sort: { order: DESC, fields: [frontmatter___date] },
      filter: { frontmatter: { templateKey: { eq: "blog-post" } }}
    ) {
      edges {
        node {
          excerpt(pruneLength: 400)
          id
          fields {
            slug
          }
          frontmatter {
            title
            image
            author
            templateKey
            date(formatString: "MMMM DD, YYYY")
          }
        }
      }
    }
  }
`;

image や author などデフォルトにない項目がありますが、次で説明します。

[Netlify CMS] 管理側エディタ・ブログコンテンツをカスタマイズする

デフォルトでは画像などをブログに追加できませんでした。
項目を追加するためにいは static/admin/config.yml を編集します。

backend:
  name: git-gateway
  branch: master

media_folder: static/img
public_folder: /img
publish_mode: editorial_workflow

collections:
  - name: "blog"
    label: "Blog"
    folder: "src/pages/blog"
    create: true
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
    fields:
      - {label: "Template Key", name: "templateKey", widget: "hidden", default: "blog-post"}
      - {label: "Title", name: "title", widget: "string"}
      - {label: "Author", name: "author", widget: "string"}
      - {label: "Publish Date", name: "date", widget: "datetime"}
      - {label: "Description", name: "description", widget: "text"}
      - {label: "Body", name: "body", widget: "markdown"}
      - {label: "Tags", name: "tags", widget: "list"}
      - {label: "Image", name: "image", widget: "image"}

上記のように設定することで、画像や著者名の項目を扱えるようになりました。

さらに、

publish_mode: editorial_workflow

を追加することで、

記事を保存していきなり投稿するのではなくて、 保存後にPull Requestが作られ、レビューしてもらってから投稿するような流れの切り替えをDnDで行えてしまいます。

設定した画像を使ってOGP対応する方法は
Gatsby + Netlify CMSで作ったブログをカスタマイズする
がとても参考になります。

最後に

Netlify CMSを使うことで驚くほど簡単にJAMStackなブログを作ることができました。
Gatsbyと使うことで見た目部分をReactで書くことができ、カスタマイズできてうれしいです。
NetlifyもNetlify CMSもまだまだカスタマイズできそうなので、これからも触っていきたいです。
個人的にはFusic Tech BlogもNetlify CMSを使ったりJAMstackで構築するのもありなのかなと思っています。

業務ではバックエンドでRails/Laravel、フロントエンドでVueを使って開発しています。

プライベートではAngular/React/Vueなどフロントエンド関連で遊んでます。

コメントする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です