Closed5
Contentfulのリッチテキストを色々管理する
目的
Contentful+Next.jsで見栄えの良いブログにする。
そのために、Contentfulでタグを扱う方法をまとめる。
rich-text-react-rendererを使って、APIから取得したリッチテキストを加工する
NPMはこちら
パッケージを追加する
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>
)
}
コードブロックのタグをpre>codeにする
rich-text-react-rendererの「コードブロック」はcodeタグのみで囲われている(デフォルト)
<p><code>コード</code></p>
これを、下記のように変更する。
<div>
<pre><code>コード</code></pre>
</div>
変更点は二点ある。
- codeタグを整形済みを意味する
preタグ
で先に囲ってあげたい。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/pre - また、
p
をdiv
に変更する。もしそうしないと以下のように怒られる(怒られた)。
Warning: validateDOMNesting(...): <pre> cannot appear as a descendant of <p>.
下記を参考にしてoptions
を用意したらうまくいった。
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>
)
}
@contentful/rich-text-typesを覗く
nodemoduleのrich-text-typesを覗くと、mark.d.ts
とblocks.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にもかいてありました。
おまけ
予測変換に出てくるのありがたい…
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にクローズされました