当サイトは、アフィリエイト広告を利用しています
【Next.js × TypeScript】MDX形式のcontentful記事を表示する
Next.jsでcontentfulにあるmdx形式の記事を
取得して表示する方法をまとめる。
MDXとはMarkdown + JSXのことで
端的にいうとmarkdown内でcomponentを使用することができる。
markdown内でReactのcomponentを使うことができる
例えば記事の装飾などもコンポーネントを使って行うことができる。
このブログでいうと下記はmdx用コンポーネントを作って
mdx記事内でmdx用コンポーネントを記載することで実現しています。
※このブログはGatsbyで作ってますが...
contentfulとapiを使って通信(記事取得)ができる状態であること前提として
まとめていきます。
contentfulとapiを使って記事を取得する手順について下記記事で書いていますので
もし知識がない場合は参照ください!
調べて見るとNext.jsでMDX形式を表示する方法は下記の二つあるみたい。
今回はcontentfulからMDX形式の記事データを取得するため
next-mdx-remoteを使う。
まずはnext-mdx-remoteを使ってmdxを扱う方法をまとめる。
動作確認環境は下記参照
"dependencies": {"@emotion/babel-plugin": "^11.9.5","@emotion/react": "^11.9.3","contentful": "^9.1.33","contentful-management": "^10.10.0","contentful-typescript-codegen": "^3.2.3","dotenv": "^16.0.1","eslint-config-prettier": "^8.5.0","next": "12.2.3","next-mdx-remote": "^4.1.0","prettier": "^2.7.1","react": "18.2.0","react-dom": "18.2.0","react-hook-form": "^7.34.1"},"devDependencies": {"@types/node": "18.6.2","@types/react": "18.0.15","@types/react-dom": "18.0.6","eslint": "8.20.0","eslint-config-next": "12.2.3","typescript": "4.7.4"}
mdx形式を表示するためのパッケージをインストールする
yarn add next-mdx-remote
contetnfulからmdx形式記事を取得して表示するサンプルを作ってみる。
MDX内で使用するコンポーネントを作成する。
import { css } from '@emotion/react'export const MDXComponet = () => {// mdx用コンポーネントconst h3 = (props: any) => {const h3Style = css`margin: 2.8rem 0 1em;padding: 8px 0;padding-left: 7px;border-bottom: 3px solid #c8cbce;border-color: rgb(69, 74, 150);`// Intersection Observer APIで監視する要素のためclassをつけるreturn <h3 css={h3Style} {...props} className='highLight' />}// mdx用コンポーネントconst h2 = (props: any) => {const h2Style = css`font-size: 1.125rem;font-weight: bold;padding-top: 9px;padding-bottom: 9px;padding-left: 7px;border-left: 7px solid black;background: rgb(69, 74, 150);color: white;font-family: 'Noto Sans JP', Roboto, sans-serif;line-height: 1.2;margin-top: 20px;margin-bottom: 20px;`return <h2 css={h2Style} {...props} />}// mdx用コンポーネントをオブジェクト化const components = { h3, h2 }return components}
Next.jsでのEmotionの導入、使い方は下記記事にまとめています
MDX形式を表示できるよう変換するコンポーネントを作成する。
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'import { MDXComponet } from './MDXcomponents'export const MDXConvert = ({ article }: { article: MDXRemoteSerializeResult }) => {return (<div><MDXRemote compiledSource={article.compiledSource} components={MDXComponet()} /></div>)}
getStaticPropsでcontentfulから記事取得して、serializeし、
MDXConvertコンポーネントに記事を渡して表示させる
import type { NextPage } from 'next'import { Entry } from 'contentful'import { IBlogPostFields } from '../../@types/generated/contentful'import { client } from '../utils/contentfulClient'import { serialize } from 'next-mdx-remote/serialize'import { MDXRemoteSerializeResult } from 'next-mdx-remote'import { MDXConvert } from '../components/MDXConvert'// getStaticPropsの戻り値の型定義interface Props {mdx: MDXRemoteSerializeResultblogPosts: Entry<IBlogPostFields>}// SSGでcontetnfulの記事を取得export const getStaticProps = async () => {// contentfulから記事取得(IDはcontetntfulで調べる)const blogPosts: Entry<IBlogPostFields> = await client.getEntry('xxxxxxxxxxxxxxxxxxxxxx')// シリアライズconst mdxSource: MDXRemoteSerializeResult = await serialize(blogPosts.fields.body || '')// 戻り値設定return {props: {blogPosts: blogPosts,mdx: mdxSource,},revalidate: 1,}}const Home: NextPage<Props> = (props) => {// 記事(mdx)を分割代入const { mdx }: { mdx: MDXRemoteSerializeResult } = propsreturn (<><h1>Contetfulの記事(html)</h1><MDXConvert article={mdx} /></>)}export default Home
これでMDXを表示できる
contentfulからmdx形式の記事データをcontentfulAPIを使って取得し
表示する方法のみをピックアップしてまとめているため
実際に使う場合は適宜、追加、変更が必要かと思います。
※実際にはcontentfulからハードコーディングでid指定で記事取得とかはしないと思うので...
当ブログ(Gatsby製)でもMDX形式で記事を書いていてデザインや埋め込みを割と自由に
できるのでMDXはかなり使いやすいです。
GatsbyでMDXを扱う方法もいずれ記事にする予定です。