Headless CMS(Prismic) + Gatsby + Netlifyでブログを作成した

2020-05-11

Prismic.io

予てから、jamstackの開発に非常に興味がありました。Gatsbyだけで作りきることも考えましたが、Headless CMSも導入したく今回はPrismic.ioを採用しました。Headless CMSはその他にも Contentful, Micro CMS, Netlify CMS などが挙げられますが、Prismic.ioを導入したのは以下の理由です。

  1. 個人の開発なら無料枠で使用が可能
  2. ダッシュボードが美しく、動きが良い
  3. 本社がフランス

言わなくてもお分かり頂けると思いますが、理由の99%は3です。というのは半分冗談で、Headless CMSが個人開発では無料で使用出来る点が個人的には大きかったです。

Prismicの導入に関してはこちらのUdemyを参考にしました。Gatsby JS, Prismic & Netlify: Build RAPID Gatsby sites 2020

Prismic.io + Gatsby

ここからは Prismic + Gatsbyでのプロジェクト作成方法を簡単に解説します。

gatsby new コマンドでプロジェクトを作成した後、gatsbyでprismic APIが使用出来るように gatsby-prismic-source-graphqlをインストールします。

npm install gatsby-source-prismic-graphql

続いてインストールした内容を反映させるために、gatsby-configに設定を反映させていきます。

plugins: [
{
resolve: 'gatsby-source-prismic-graphql',
options: {
repositoryName: 'prismicで作ったプロジェクトのサブドメイン'
}
},
...
]

ここまで完了して graphqlのツールを確認すると以下の項目が増えています。

  • allGraphQlSourcePrismic
  • graphQlSourcePrismic
  • prismic

さらに詳しい内容はPrismic.ioの公式ページUsing Prismic with Gatsbyで確認出来ます。

Prismic + Gatsbyでのポイント

・RichTextを使用する場合はGatsby Linkを使ってprefetchする

Prismicで作られたRichTextデータをレンダリングするにはpriscmi-react.jsをインストールしRechTect Componentを使用する必要があります。

npm install prismic-reactjs --save

RechTextを使用するコンポーネントには以下のように記述が必要です。

import { RichText } from 'prismic-reactjs';

ここでのポイントは、内部リンクと外部リンクの切り分けです。Gatsby Linkの最大のメリットは prefetchです。

prefetch機能を最大限に活かすために、prismic-reactjsで提供されているデフォルトのRichTextを使うのではなく、RichTextを以下の2つのファイルを追加してカスタムしました。

/*richText.js*/
import React from 'react';
import { Link as PrismicLink, RichText } from 'prismic-reactjs'
import { Link } from 'gatsby';
import {linkResolver} from '../utils/linkResolver';

const htmlSerializer = (type, element, content, children, index) => {
// Generate links to Prismic Documents as <Link> components
if (type === 'hyperlink') {
let result = ''
const url = PrismicLink.url(element.data, linkResolver)
if (element.data.link_type === 'Document') {
result = <Link to={ url } key={ index }>{ content }</Link>
} else {
const target = element.data.target ? { 'target':element.data.target, 'rel':'noopener' } : {}
result = <a href={ url } { ...target } key={ index }>{ content }</a>
}
return result
}

// If the image is also a link to a Prismic Document, it will return a <Link> component
if (type === 'image') {
let result = <img src={ element.url } alt={ element.alt || '' } copyright={ element.copyright || '' } />

if (element.linkTo) {
const url = PrismicLink.url(element.linkTo, linkResolver)

if (element.linkTo.link_type === 'Document') {
result = <Link to={ url } key={ index }>{ result }</Link>
} else {
const target = element.linkTo.target ? { 'target':element.linkTo.target, 'rel':'noopener' } : {}
result = <a href={ url } { ...target }>{ result }</a>
}
}
const wrapperClassList = [element.label || '', 'block-img'];
result = <p className={ wrapperClassList.join(' ') } key={ index }>{result}</p>
return result;
}

// Return null to stick with the default behavior for everything else
return null;
};

const RichTextCustom = ({render}) => {
return (
<RichText
render={render}
linkResolver={linkResolver}
htmlSerializer={htmlSerializer} />
);
}

export default RichTextCustom;
/*linkResolver.js*/
export const linkResolver = (doc) => {
if(doc.type === 'page'){
return `/${doc.uid}`;
}
return '/';
};

この2つのファイルを追加することで、RichText内にある内部リンクがprefetchされるため、ユーザビリティーが向上します。

Prismic + Gatsbyでのハマりどころ

・画像ファイルがぼやけて綺麗に表示されない

Netlifyにデプロイの後で Cannot read property 'childImageSharp' of undefined とコンソールにエラーログが表示され、画像がぼやけた状態でロードされました。useStateも動作しない状態で、解決するまで時間を要したのでハンドリング方法を共有します。

結論としてPrismicでは2020年5月の段階でuseStaticQueryが使用出来ないことが原因でした。

現状はuseStaticQueryではなくStaticQueryを代わりに使用することで対処しています。ここに関してはPrismicがuseStaticQueryを1日でも早く使えるようにしてくれる事を願います。

/*変更前*/
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const Image = () => {
const data = useStaticQuery(graphql`
query {
placeholderImage: file(relativePath: { eq: "laptop_header_image.png" }) {
childImageSharp {
fluid(maxWidth: 1500) {
...GatsbyImageSharpFluid
}
}
}
}
`)

return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
/*変更後*/
import React from "react"
import { StaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"

const query = graphql`
query {
placeholderImage: file(relativePath: { eq: "laptop_header_image.png" }) {
childImageSharp {
fluid(maxWidth: 1500) {
...GatsbyImageSharpFluid
}
}
}
}
`
const Image = () => (
<StaticQuery
query={`${query}`}
render={data => {
return <Img fluid={data.placeholderImage.childImageSharp.fluid} />
}
}
/>
)

export default Image

これで問題なく動きました。

今回のGatsby + Prismic + Netlifyを通じてかかった費用はカスタムドメインのみです。安く、素早くプロジェクトを立ち上げられる jamstackの開発は今後加速していくと思います。