当サイトは、アフィリエイト広告を利用しています

【Next.js × TypeScript】MDX形式のcontentful記事を表示する

作成日:2022月08月16日
更新日:2022年09月19日

【Next.js × TypeScript】MDX形式のcontentful記事を表示する

Next.jsでcontentfulにあるmdx形式の記事を
取得して表示する方法をまとめる。

MDXとは?

MDXとはMarkdown + JSXのことで
端的にいうとmarkdown内でcomponentを使用することができる。

MDXのメリット

markdown内でReactのcomponentを使うことができる
例えば記事の装飾などもコンポーネントを使って行うことができる。
このブログでいうと下記はmdx用コンポーネントを作って
mdx記事内でmdx用コンポーネントを記載することで実現しています。
※このブログはGatsbyで作ってますが...

  • codesandboxのコード埋め込み
  • paize.ioのコード埋め込み
  • 関連記事を表示する部分

前提条件

contentfulとapiを使って通信(記事取得)ができる状態であること前提として
まとめていきます。
contentfulとapiを使って記事を取得する手順について下記記事で書いていますので
もし知識がない場合は参照ください!

Next.jsでMDXを使う方法

調べて見るとNext.jsでMDX形式を表示する方法は下記の二つあるみたい。

  • next-mdx-remoteを使う
  • @next/mdxのMDXProviderを使う

今回はcontentfulからMDX形式の記事データを取得するため
next-mdx-remoteを使う。

next-mdx-remoteを使う

まずはnext-mdx-remoteを使ってmdxを扱う方法をまとめる。

環境

動作確認環境は下記参照

package.json
"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形式を表示するためのパッケージをインストールする

js
yarn add next-mdx-remote

MDX形式記事をNext.jsで表示する

contetnfulからmdx形式記事を取得して表示するサンプルを作ってみる。

MDXComponent

MDX内で使用するコンポーネントを作成する。

MDXComponet
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
}
  • h2,h3タグのスタイリングをするMDXコンポーネントを作成
  • styleはEmotionでしている
  • MDX変換用のコンポーネントに渡す時はオブジェクトの形となるため、オブジェクト形でretrunする

Next.jsでのEmotionの導入、使い方は下記記事にまとめています

MDXConvert

MDX形式を表示できるよう変換するコンポーネントを作成する。

MDXConvert
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>
)
}
  • MDX内で使用するコンポーネントはpropsのcomponentsで全て渡してやる必要がある
  • propsのcompiledSourceにはserializeした値を渡す必要がある※後述

page/index.tsx

getStaticPropsでcontentfulから記事取得して、serializeし、
MDXConvertコンポーネントに記事を渡して表示させる

index.tsx
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: MDXRemoteSerializeResult
blogPosts: 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 } = props
return (
<>
<h1>Contetfulの記事(html)</h1>
<MDXConvert article={mdx} />
</>
)
}
export default Home

これでMDXを表示できる

実行結果イメージ

next.jsのmdxサンプル

参考

まとめ

contentfulからmdx形式の記事データをcontentfulAPIを使って取得し
表示する方法のみをピックアップしてまとめているため
実際に使う場合は適宜、追加、変更が必要かと思います。
※実際にはcontentfulからハードコーディングでid指定で記事取得とかはしないと思うので...
当ブログ(Gatsby製)でもMDX形式で記事を書いていてデザインや埋め込みを割と自由に
できるのでMDXはかなり使いやすいです。
GatsbyでMDXを扱う方法もいずれ記事にする予定です。

新着記事

タグ別一覧
top