Next.js製 個人ブログに zenn-markdown-html と zenn-content-css を導入する
Next.js でビルド時に静的コンテンツを生成し、成果物を Amazon S3 などでホスティングすれば Webページとして公開できます。個人のブログを公開するハードルが下がりました。よくあるパターンとしては、Markdown ファイルを記事のソースにし、Static Site Generator で 静的サイトを生成する手法です。blog-starter-typescript などの雛形も用意されており、始めやすいですよね。ただ、デフォルトの状態から発展させていくときに、手詰まりになりがちです。私も試みたことがあるのですが、 素の Markdown だと表現力に欠けるけどカスタマイズするハードルが高いなあ だったり、見た目を整えたいけどCSSとじっくり向き合う時間がない という理由でブロク構築が止まってしまうことがありました。
ZennのMarkdown記法一覧 でも記載されているように、Zennでパースできる記法はバリエーションが豊富です。自分でmarkdown-itなどをカスタマイズして対応させようと思うと結構大変なので、公開されている Zenn のパーザーを使ってみましょう。この記事では、公式の Next.js ブログサンプルをもとに、Zenn でも利用している zenn-markdown-html と zenn-content-css 適用する例を示します。 MarkdownパーザーとブログサイトのCSSで困っている方はご参照ください。
適用する流れ
- blog-starter でひな形を作成
- zenn-markdown-html、zenn-content-css、zenn-embed-elements を導入する
- ローカルで適用されたことを確認する
今回はローカルで試します。
バージョン情報
利用ツール | バージョン |
---|---|
Next.js |
|
zenn-markdown-html | 0.1.81 |
zenn-content-css | 0.1.81 |
zenn-embed-elements | 0.1.81 |
blog-starter でひな形を作成
1.作業ディレクトリで素直に導入します。
yarn create next-app --example blog-starter .
yarn dev
こんな感じの画面が見えればOKです。
少し構成を覗いてみましょう。Markdown を HTML に変換している箇所が導入ポイントになるはずです。
export async function getStaticProps({params}: Params) {
const post = getPostBySlug(params.slug, [
'title',
'date',
'slug',
'author',
'content',
'ogImage',
'coverImage',
])
const content = await markdownToHtml(post.content || '')
return {
props: {
post: {
...post,
content,
},
},
}
}
ここで Markdown を HTML に変換しているようです。まだ zenn 系のツールは入れていないので、Zenn の独自記法はうまく表示されないはずです。独自記法のページのソースを拝借して、Markdwon 文書として_posts
ディレクトリに配置してみましょう。
# Zenn 独自の記法
### メッセージ
```
:::message
メッセージをここに
:::
```
:::message
メッセージをここに
:::
```
:::message alert
警告メッセージをここに
:::
```
:::message alert
警告メッセージをここに
:::
当たり前ですがブログのサンプルに同じMarkdownを入れてもまだZennのように表示はされないですね。これを表示できるように改修します。
zenn-markdown-html、zenn-content-css、zenn-embed-elements を導入する
2.それぞれの Readme に導入方法が記載されていますが、ざっくり説明すると以下のような感じです。
zenn-markdown-html
- Markdown コンテンツを HTML に変換する箇所で zenn 側の
markdownToHtml
関数を使う
zenn-content-css
- CSSを適用したいコンポーネントやブロックに
className=znc
を指定する -
className=znc
を記載したtsx
ないしjsx
でimport 'zenn-content-css';
する- アプリ全域で使うのであれば
App
コンポーネントでimportしてもOK
- アプリ全域で使うのであれば
zenn-embed-elements
- ブラウザ側で埋め込みコンテンツ(数式)を変換します。
App
コンポーネントでuseEffect(() => import('zenn-embed-elements'), [])
と書く
ブログサンプルの変更点
"dependencies": {
...
+ "zenn-content-css": "^0.1.131",
+ "zenn-markdown-html": "^0.1.131",
+ "zenn-embed-elements": "^0.1.131"
}
その後、yarn install
。
- import markdownToHtml from '../../lib/markdownToHtml'
+ import markdownToHtml from 'zenn-markdown-html';
type Props = {
post: PostType
morePosts: PostType[]
preview?: boolean
}
const Post = ({post, morePosts, preview}: Props) => {
const router = useRouter()
if (!router.isFallback && !post?.slug) {
return <ErrorPage statusCode={404}/>
}
return (
<Layout preview={preview}>
<Container>
<Header/>
{router.isFallback ? (
<PostTitle>Loading…</PostTitle>
) : (
<>
- <article className="mb-32" >
+ <article className="mb-32 znc" > // CSSが反応できるようにクラスを追加します
<Head>
<title>
{post.title} | Next.js Blog Example with {CMS_NAME}
</title>
<meta property="og:image" content={post.ogImage.url}/>
</Head>
<PostHeader
title={post.title}
coverImage={post.coverImage}
date={post.date}
author={post.author}
/>
<PostBody content={post.content}/>
</article>
</>
)}
</Container>
</Layout>
)
}
import { AppProps } from 'next/app'
import { useEffect } from 'react';
import '../styles/index.css'
+ import 'zenn-content-css';
export default function MyApp({Component, pageProps}: AppProps) {
+ useEffect(() => {
+ import("zenn-embed-elements"); // 数式をブラウザでレンダリングできるようにします
+ }, []);
return <Component {...pageProps} />
}
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="en">
- <Head />
+ <Head>
+ <script src="https://embed.zenn.studio/js/listen-embed-event.js"></script> // 一部の埋め込みで、高さ調節のためにスクリプトを利用します
+ </Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
3.ローカルで適用されたことを確認する
これでOK…?起動して確認してみましょう。
yarn dev
open http://localhost:3000/posts/zenn-markdown
Zenn の独自記法のところ、パースされてスタイルもあたってますね。
埋め込みコンテンツを試す
他の記法も確認しましょう。
Youtube
https://www.youtube.com/watch?v=WRVsOCh907o
https://twitter.com/jack/status/20
数式(KaTeX)
$$
e^{i\theta} = \cos\theta + i\sin\theta
$$
GitHub Gist
すべて意図どおりに埋め込まれています。インストールして少しコードを変更しただけで導入できるのでありがたいですね。
おわりに
Zenn の公開されているツールをサンプルブログに適用してみました。タイトル周りや一覧画面はさらに改修が必要かと思いますが、本文には意図どおり反映できました。Next.js を使って個人ブログを構築したいけど、パーザーや記事のCSSで詰まっている…という方は、導入を検討してみてください。そして、「こういうのもパースしたいな〜」という気持ちが出てきたらzenn-dev/zenn-editorにプルリクを送ると歓迎されると思います!
Discussion
個人ブログに導入させていただきました!
全体的なスタイリングはもちろん、リンクをカードにしてくれたり、codesandboxなどのサービスを簡単に埋め込めたりとてもありがたいです!
こちらの記事を参考に導入するときに、こちらのissueの問題に当たったので共有いたします。
こちらの記事と同じことをする場合は
zenn-markdown-html
,zenn-content-css
,zenn-embed-elements
のバージョンをv0.1.106
に設定する必要があります。現在は最新バージョンで利用できるようです
うおおおコメントを見逃しており申し訳ないです...!こちらでもご指摘頂いていたのですね!zenn-community への報告、ありがとうございました!ご存知のとおり、現在は最新バージョンが利用できるように整備済みです。