Next.jsでブログを作ってみました
概要
Next.js, react-markdown, react-syntax-highlighter, tailwindCSS, Figmaらを使って簡単な機能があるブログを作ってみました。
Vercelを使って公開しています。(※ サンプル記事はzennに書いた記事をいくつか入れているため、画像のurlにzennになっています。)
主なポイントは
- DBは使わずマークダウンで記事を書くとhtmlに反映
- 記事にtagをつけてtag一覧を生成
- 記事のイメージをS3などの外部Storageは使わない(githubでマークダウンファイルを作って記事を直接書くと、イメージもアップロードしてURLが生成されるので)
- 記事のサムネイルだけはpublic配下におく
機能一覧
最小限の機能を入れています。
- マークダウン記事 → HTML表示
- 記事内のcode blockに色付き
- imageはimgタグを利用することでsize調節できるようにする
- 目次表示
- ロゴのアニメーション
- サイドメニュー表示
- profileページ作成
- tag機能追加
- レスポンシブ
- SEO(meta tag, lighthouse, image lazy-load, robot.txt)
今後追加したいもの
- dark mode
- 共有機能
- 検索機能
- 記事の公開非公開機能
- コメント機能
- thumbnailジェネレータ
- E2Eテスト
実装方法
マークダウン記事 → HTML表示
最初はremark-htmlとdangerouslySetInnerHTML使ってマークダウンをhtml化しましたが、react-markdownに変えました。
react-markdownの方が色んなプラグインをつけやすいし、全体的に簡単に書けたからです。
<ReactMarkdown
rehypePlugins={[rehypeRaw]}
components={CodeBlock}
remarkPlugins={[gfm]}
className="prose prose-stone mt-5 max-w-4xl m-auto"
>
{post.content}
</ReactMarkdown>
rehype Pluginsで使いたいプラグインをnpm installして、rehypePlugins
propsに追加します。
markdownの中でもHtmlタグが使えるように rehype-raw
を追加しました。
Tailwind CSS
CSSはTailwindCSSを使いました。
巣のCSSのように自由にデザインができるけど、簡単に統一したデザインができるし、個人的に使い慣れているからです。
マークダウンのスタイリング
< 前 >
< 後 >
マークダウンをhtmlにする時に、TailwindCSSのtypography-pluginを利用すると簡単にデザインが適応されます。
@tailwindcss/typographyをインストールしてmarkdownを囲んでいるクラスにproseとつけるだけです。
<article class="prose prose-xl">
{{ markdown }}
</article>
上記の ReactMarkdown
のclass名にも同じくつけています。
レスポンシブ
tailwind.config.js
ファイルに画面サイズを定義して置いて、
theme: {
screens: {
tablet: "640px",
laptop: "1024px",
desktop: "1280px",
},
}
classで tablet:grid
のように利用します。以下の例は記事リストのカード配置のclassです。
<div className="flex flex-col justify-center space-y-8 tablet:grid tablet:grid-cols-2 tablet:gap-2 tablet:space-y-0 desktop:grid-cols-3">
記事内のcode blockのスタイリング
< 前 >
< 後 >
react-syntax-highlighter
を利用しました。
react-markdownでコードをシンタックスハイライトさせるフログを参考にしました。
まずはcodeblockを整形するコンポーネントを作ります。
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { a11yDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { CodeComponent } from "react-markdown/lib/ast-to-react";
const customCode: CodeComponent = ({ inline, className, children }) => {
const style = a11yDark;
const match = /language-(\w+)(:?.+)?/.exec(className || "");
const lang = match && match[1] ? match[1] : "";
const name = match && match[2] ? match[2].slice(1) : "";
return !inline && match ? (
<>
{name && <span className="bg-stone-200 py-1 px-2 text-xs">{name}</span>}
<SyntaxHighlighter
style={style}
language={lang}
PreTag="div"
className="md-codeblock"
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
</>
) : (
<code className="rounded-md bg-stone-200 text-red-600">{children}</code>
);
};
export const CodeBlock = {
code: customCode,
};
色んな highlighter styleの指定が可能です。私は a11yDark
を選びました。
prism-themes
ReactMarkdown propsの components
に作ったCodeBlockを追加するだけでOK
<ReactMarkdown
rehypePlugins={[rehypeRaw]}
components={CodeBlock}
remarkPlugins={[gfm]}
className="prose prose-stone mt-5 max-w-4xl m-auto"
>
{post.content}
</ReactMarkdown>
ReactMarkdownのcomponentは特定のhtmlタグをカスタマイズすることができます。
例えばh1をh2にするとか、imgタグの中身を取得してリンクにするなどができます。
目次を作る
二つ方法を試しましたが、後者の方のrehype-toc
を利用しました。
remark-tocを利用
最初は remark-tocを使って目次を作る方法です。導入はとても簡単でremark-toc
とrehype-slug
をinstallし、
ReactMarkdownのremarkPlugins
とrehypePlugins
にそれぞれ追加するだけです。
remark-toc
はhtmlからtocを作ってくれるもので、rehype-slug
はh1, h2のような見出しタグにidを付与して目次のリンクから各項目に飛べるようにしてくれます。
$ npm install remark-toc
$ npm install rehype-slug
<ReactMarkdown
rehypePlugins={[rehypeRaw, rehypeSlug]}
components={CodeBlock}
remarkPlugins={[gfm, [remarkToc, { maxDepth: 2, heading: "目次" }]]}
remarkPlugins={[gfm]}
className="prose prose-stone mt-5 max-w-4xl m-auto">
{post.content}
</ReactMarkdown>
以下のように、マークダウンの中に「目次」と書いてあるところの下に作られます。(装飾は自分のでデザインに引っ張られているだけ。)
rehype-tocを利用
rehype-toc
とrehype-slug
を使って目次を作る方法です。
同じくnpmでinstallし、今回は両方ともrehypePlugins
に追加します。
rehype-toc
rehype-tocはより多くのオプションが指定できました。tocOptions
として別途optionを作りました。
cssClasses
オプションを使ってclass名をつけると、それを付与したtocが作られます。
これで目次のデザインも自由にできます。
const tocOptions = {
headings: "h2",
cssClasses: {
toc: "prose-toc",
list: "prose",
listItem: "prose-toc-list-item",
link: "prose-toc-link",
},
};
<ReactMarkdown
rehypePlugins={[rehypeRaw, rehypeSlug, [rehypeToc, tocOptions]]} // rehypeTocとそのoptionを渡す
components={CodeBlock}
remarkPlugins={[gfm]}
className="prose prose-stone mt-5 max-w-4xl m-auto"
>
{post.content}
</ReactMarkdown>
作られる目次の中身
<nav className="prose-toc">
<ol className="prose prose-1">
<li className="prose-toc-list-item prose-toc-list-item-h2"><a
className="prose-toc-link prose-toc-link-h2" href="#概要">概要</a></li>
<li className="prose-toc-list-item prose-toc-list-item-h2"><a
className="prose-toc-link prose-toc-link-h2" href="#state-of-css-2022">State of CSS 2022</a>
</li>
<li className="prose-toc-list-item prose-toc-list-item-h2"><a
className="prose-toc-link prose-toc-link-h2" href="#まとめ">まとめ</a></li>
</ol>
post.contentの一番上に自動で目次が作られます。
lighthouse / SEO
Chrome ブラウザの lighthouse
を回して引っかかったものを追加しました。
meta tag、robot.txt, imageのlazyloadなど色々指摘してくれたので修正しました。
最後
ブログでもなんでもいいと思いますが、自分のWebpageを持っていると自由に遊べるテーマパークができたようで楽しいです。
もっと整ったらソースコードも公開して見ようと思います。
Discussion