💡

【Next.js】Next.js & Contentful BlogApp 【13PostDetailsPage4】

2022/11/29に公開

【13PostDetailsPage4】

YouTube:https://youtu.be/2sacVyWRcGs

https://youtu.be/2sacVyWRcGs

今回はブログ本文のスタイリングについて実装を進めていきます。

今回はライブラリの解説がメインになります。
こちらの記事からオプションファイルのコピーをお願い致します。

内容が難しいと感じた場合は、3:35までで実装が完了した後は
10:50まで説明を飛ばして、
最後の注意の部分だけ見て、次に進んでも大丈夫です。

まずは必要なライブラリを2つインストールから始めます。

https://www.npmjs.com/package/@contentful/rich-text-types

https://www.npmjs.com/package/react-syntax-highlighter

カスタマイズの対象となる要素のタイプを取得するライブラリ
npm i @contentful/rich-text-types

コードの部分をエディター風にスタイリングするライブラリ
npm i react-syntax-highlighter

package.json
  "dependencies": {
    "@contentful/rich-text-types": "^15.14.1",
    "react-syntax-highlighter": "^15.5.0"
  },

一番上の階層に「lib」フォルダを作成して、
内部に「richTextOptions.js」を作成します。

今回はライブラリの解説をメインに進めますので、
まずはこちらのコードをすべてコピーしてください。

lib/richTextOptions.js
import { BLOCKS, MARKS } from '@contentful/rich-text-types'

import SyntaxHighlighter from 'react-syntax-highlighter'
import { monokaiSublime } from 'react-syntax-highlighter/dist/cjs/styles/hljs'

const CustomHeading_1 = ({ children }) => (
  <h2 className="text-3xl font-black mb-2">{children}</h2>
)
const CustomHeading_2 = ({ children }) => (
  <h3 className="text-2xl font-black mb-2">{children}</h3>
)
const CustomParagraph = ({ children }) => (
  <p className="text-xl mb-1">{children}</p>
)

export const options = {
  renderMark: {
    [MARKS.CODE]: (text) => (
      <SyntaxHighlighter
        language="javascript"
        style={monokaiSublime}
        className="rounded-md text-sm"
      >
        {text}
      </SyntaxHighlighter>
    ),
  },
  renderNode: {
    [BLOCKS.HEADING_1]: (node, children) => (
      <CustomHeading_1>{children}</CustomHeading_1>
    ),
    [BLOCKS.HEADING_2]: (node, children) => (
      <CustomHeading_2>{children}</CustomHeading_2>
    ),
    [BLOCKS.PARAGRAPH]: (node, children) => {
      if (
        node.content.length === 1 &&
        node.content[0].marks.find((x) => x.type === 'code')
      ) {
        return <div>{children}</div>
      }
      return <CustomParagraph>{children}</CustomParagraph>
    },
  },
}

次にこちらのオプションを「[slug].js」でインポートして
「documentToReactComponents(item.fields.content)」の第2引数に設定します。

pages/posts/[slug].js
import Image from 'next/image'
import Layout from '../../components/Layout'
import { client } from '../../utils/contentfulClient'
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'
import { options } from '../../lib/richTextOptions'

export const getStaticPaths = async () => {
  const res = await client.getEntries({
    content_type: 'myPosts',
  })
  const paths = res.items.map((item) => {
    return {
      params: { slug: item.fields.slug },
    }
  })

  return {
    paths,
    fallback: 'blocking',
  }
}

export const getStaticProps = async ({ params }) => {
  const { items } = await client.getEntries({
    content_type: 'myPosts',
    'fields.slug': params.slug,
  })

  if (!items.length) {
    return {
      notFound: true,
    }
  }

  return {
    props: { item: items[0] },
    revalidate: 10,
  }
}

const PostDetailsPage = ({ item }) => {
  console.log(item.fields.content)
  return (
    <Layout title={item.fields.slug}>
      <div className="w-full">
        <div className="max-w-6xl flex flex-wrap justify-between items-center mx-auto py-6 px-5 gap-3">
          <div>
            <h1 className="font-bold text-3xl sm:text-4xl md:text-4xl lg:text-5xl py-3 mb-1">
              {item.fields.title}
            </h1>
            <p className="font-bold text-2xl mb-2">
              {item.sys.createdAt.substring(0, 10)}
            </p>
          </div>
          <div>
            <Image
              src={`https:${item.fields.image.fields.file.url}`}
              alt={item.fields.image.fields.description}
              width={500}
              height={500}
              className="rounded"
            />
          </div>
        </div>
      </div>
      <div className="max-w-6xl mx-auto p-5">
        {documentToReactComponents(item.fields.content, options)}
      </div>
    </Layout>
  )
}

export default PostDetailsPage

Discussion