讓 Contentlayer 文章支援 MDX - Modern Next.js Blog 系列 #08

Published on

本文同步發佈於 it 邦幫忙 2022 iThome 鐵人賽

TL;DR

這是「Modern Blog 30 天」系列第 8 篇文章,上一篇我們實作了 Markdown 格式的文章內頁,這篇我們會擴充它,讓他支援 MDX 格式,能在 Markdown 內插入任何 React 元件,提升文章內容靈活度!

結果截圖如下:

MDX post result

這篇修改的程式碼如下:

https://github.com/Kamigami55/nextjs-tailwind-contentlayer-blog-starter/compare/day07-post-apge-bare-bone...day08-mdx-support


讓 Contentlayer 支援 MDX 格式

修改 contentlayer.config.ts 檔案

修改 filePathPattern 檔名,以及新增 contentType:

import { defineDocumentType, makeSource } from "./src/lib/contentLayerAdapter"; export const Post = defineDocumentType(() => ({ name: "Post", // 更新 filePathPattern,從 *.md 改成 *.mdx, filePathPattern: `content/posts/**/*.mdx`, // 並新增下面這行 contentType contentType: "mdx", fields: { title: { type: "string", required: true, }, description: { type: "string", required: true, }, slug: { type: "string", required: true, }, date: { type: "date", required: true, }, }, computedFields: { path: { type: "string", resolve: (post) => `/posts/${post.slug}`, }, }, })); export default makeSource({ contentDirPath: "content", documentTypes: [Post], });

修改文章內頁的 src/pages/posts/[slug].tsx 檔案

替換掉 render html 的 div,改成 render <MDXContent />

import { format, parseISO } from "date-fns"; import type { GetStaticPaths, GetStaticProps, NextPage } from "next"; import Head from "next/head"; // 新增下面這行 import { useMDXComponent } from "next-contentlayer/hooks"; import { allPosts, Post } from "@/lib/contentLayerAdapter"; import styles from "@/styles/Home.module.css"; export const getStaticPaths: GetStaticPaths = () => { const paths = allPosts.map((post) => post.path); return { paths, fallback: false, }; }; export const getStaticProps: GetStaticProps<Props> = ({ params }) => { const post = allPosts.find((post) => post.slug === params?.slug); if (!post) { return { notFound: true, }; } return { props: { post, }, }; }; type Props = { post: Post; }; const PostPage: NextPage<Props> = ({ post }) => { // 以及新增下面這行 const MDXContent = useMDXComponent(post.body.code); return ( <div className={styles.container}> <Head> <title>{post.title}</title> <meta name="description" content={post.description} /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.main}> <h1 className={styles.title}>{post.title}</h1> <time dateTime={post.date}> {format(parseISO(post.date), "LLLL d, yyyy")} </time> // 替換掉 render html 的 div,改為 <MDXContent /> // <div dangerouslySetInnerHTML={{ __html: post.body.html }} /> <MDXContent /> </main> </div> ); }; export default PostPage;

將所有文章副檔名由 .md 改成 .mdx

將所有 content/posts/ 底下的文章副檔名改成 .mdx

如此一來就完成 MDX 格式支援了!

新增任意 React 元件,並插入 MDX 文章內

接著讓我們來測試將 React 元件放進 MDX 文章的 Markdown 內容之間。

新增 src/components/CustomInput.tsx

const CustomInput = () => { return ( <div> <p>Hello</p> <input type="text" /> </div> ); }; export default CustomInput;

修改 content/posts/20220831-markdown-demo.mdx

插入剛剛的 CustomInput 元件:

--- title: Markdown demo description: This is a demo of Markdown slug: markdown-demo date: 2022-08-31 type: Post --- import CustomInput from "../../src/components/CustomInput"; ## H2 title ### H3 title Some content with [link](https://www.google.com) <CustomInput />

成果

完成了!使用 pnpm dev 並進入有客製化 MDX 元件的文章,就會看到 MDX 元件顯示在畫面上了。

網址會長得像這樣:

http://localhost:3000/posts/markdown-demo

截圖如下,可以看到多了一個用 React 做成的文字輸入框:

MDX post result

這篇修改的程式碼如下:

https://github.com/Kamigami55/nextjs-tailwind-contentlayer-blog-starter/compare/day07-post-apge-bare-bone...day08-mdx-support

下一篇

恭喜你!目前為止我們也能用 MDX 格式撰寫文章了!能在任意地方插入任何 React 做得出來的客製化元件,讓文章內容的靈活度更好!

但目前整個 Blog 還挺醜的,接下來讓我們著手開始美化樣式吧!

我們會使用到 Tailwind CSS 這套最近很熱門的 CSS Framework,下一篇讓我們來安裝並使用它!