Closed5

Contentfulのリッチテキストを色々管理する

shunshun

目的

Contentful+Next.jsで見栄えの良いブログにする。
そのために、Contentfulでタグを扱う方法をまとめる。

shunshun

rich-text-react-rendererを使って、APIから取得したリッチテキストを加工する

NPMはこちら
https://www.npmjs.com/package/@contentful/rich-text-react-renderer

パッケージを追加する

npm install @contentful/rich-text-react-renderer

documentToReactComponentsをインポートする
documentToReactComponents(記事)でいい感じに記事がタグで囲われる。

import { documentToReactComponents } from '@contentful/rich-text-react-renderer'
// body: APIから取得した記事情報
function Content({ body }) {
  return (
      <div className="content">
        {documentToReactComponents(body)}
      </div>
  )
}
shunshun

コードブロックのタグをpre>codeにする

rich-text-react-rendererの「コードブロック」はcodeタグのみで囲われている(デフォルト)

<p><code>コード</code></p>

これを、下記のように変更する。

<div>
  <pre><code>コード</code></pre>
</div>

変更点は二点ある。

  1. codeタグを整形済みを意味するpreタグで先に囲ってあげたい。
    https://developer.mozilla.org/ja/docs/Web/HTML/Element/pre
  2. また、pdivに変更する。もしそうしないと以下のように怒られる(怒られた)。
Warning: validateDOMNesting(...): <pre> cannot appear as a descendant of <p>.

下記を参考にしてoptionsを用意したらうまくいった。
https://frontsensei.com/blog/post/syntax-highlighting-on-contentful

import { BLOCKS, MARKS } from '@contentful/rich-text-types';
import { documentToReactComponents } from '@contentful/rich-text-react-renderer'

// body: APIから取得した記事情報
function Content({ body }) {
  const Code = ({children}) => <div><pre><code>{children}</code></pre></div>
  const options = {
    renderNode: {
      // コードブロックをdivで括る
      [BLOCKS.PARAGRAPH]: (node, children) => {
        if (
          node.content.length === 1 &&
          node.content[0].marks.find((x) => x.type === "code")
        ) {
          return <Code>{children}</Code>;
        }
        return <p>{children}</p>;
      },
    }
  }

  return (
      <div className="content">
        {documentToReactComponents(body)}
      </div>
  )
}
shunshun

@contentful/rich-text-typesを覗く

nodemoduleのrich-text-typesを覗くと、mark.d.tsblocks.d.tsなるものがあった。

mark.d.ts
declare const _default: {
    BOLD: string;
    ITALIC: string;
    UNDERLINE: string;
    CODE: string;
};
/**
 * Map of all Contentful marks.
 */
export default _default;
blocks.d.ts
/**
 * Map of all Contentful block types. Blocks contain inline or block nodes.
 */
declare enum BLOCKS {
    DOCUMENT = "document",
    PARAGRAPH = "paragraph",
    HEADING_1 = "heading-1",
    HEADING_2 = "heading-2",
    HEADING_3 = "heading-3",
    HEADING_4 = "heading-4",
    HEADING_5 = "heading-5",
    HEADING_6 = "heading-6",
    OL_LIST = "ordered-list",
    UL_LIST = "unordered-list",
    LIST_ITEM = "list-item",
    HR = "hr",
    QUOTE = "blockquote",
    EMBEDDED_ENTRY = "embedded-entry-block",
    EMBEDDED_ASSET = "embedded-asset-block"
}
export default BLOCKS;

typescriptは使用したことがまだないので、よくわからないが、ここで定義されているものを使用すれば色々変更できそう。

追記

上記のBLOCKSとMARKSの一覧、npmのREADMEにもかいてありました。
https://www.npmjs.com/package/@contentful/rich-text-react-renderer

おまけ

予測変換に出てくるのありがたい…

shunshun

optionsに追記する

hタグをh3から始まるように修正してみる。
意図としては、h1: ページのタイトル, h2: 記事のタイトルで見出しに使っているため。

記事の見出しはh3のHeading-3から始めたいが、Contentfulで記事を書く際に意識するのは面倒なため。Heading-1から書き始められるようにする。

Heading1->h3,Heading2->h4...とする。Heading-5以降は使わないためpタグにランクダウンさせる。

Content.js
  const H1 = ({children}) => <h3>{children}</h3>
  const H2 = ({children}) => <h4>{children}</h4>
  const H3 = ({children}) => <h5>{children}</h5>
  const H4 = ({children}) => <h6>{children}</h6>
  const H5 = ({children}) => <p>{children}</p>
  const H6 = ({children}) => <p>{children}</p>
  const options = {
    renderMark: {
      [MARKS.CODE]: text => <CodeBlock>{text}</CodeBlock>,
    },
    renderNode: {
      [BLOCKS.HEADING_1]: (node, children) => <H1>{children}</H1>,
      [BLOCKS.HEADING_2]: (node, children) => <H2>{children}</H2>,
      [BLOCKS.HEADING_3]: (node, children) => <H3>{children}</H3>,
      [BLOCKS.HEADING_4]: (node, children) => <H4>{children}</H4>,
      [BLOCKS.HEADING_5]: (node, children) => <H5>{children}</H5>,
      [BLOCKS.HEADING_6]: (node, children) => <H6>{children}</H6>,
    }
  }

BLOCKSはrenderNodeに記述する。

追記

H4以降は、Conetntfulのcontent model側でdisabledにすればええやん…って気づいた

このスクラップは2022/05/12にクローズされました