tnkzw.sake

HomeSeriesTagsAbout

Next.js App Routerでブログを作成する

Next を使って何かしてみたい。せっかくだし話題の App Router で作りたい。
さらにさらに Cloudflare Pages も気になるなぁ

と最近思っていたことを一気に叶えるためにブログを構築しました。
理解不足で色んな躓きをしたので、自分の整理をかねて何回かにわたって構築を振り返ります。

構築の目的

今回のブログ構築はざっくり以下の目的をみたしたいと考えてスタートしました。

  • Next.js(App Router)に触れられること
    • SSG, SSR, ISR, CSR の差が実装でわかること
  • Cloudflare Pages にデプロイすること
  • リポジトリ内で markdown を編集してそれをデプロイする形式であること

シンプルに何か作ってみたかったというのも大きいです。
どうしても仕事の範囲だけだと気になっているけど触ったことない技術が出てしまうので、こういう場は必要だと感じます。

計画を練る

ざっくりこんな感じで事例があったのでいけそうだ!と確信。
まずは動かしてテンションを上げて行きたいので早速取り組みます。

構築

作業手順と引っかかったこと中心に記録します。

立ち上げ

プロジェクトを立ち上げましょう。
今回はyarnの v4 系を利用する前提で進めます。

yarn create next-app --example blog-starter {project-name}

コマンドを実行したディレクトリにproject-nameフォルダが生成されます。
デフォルトで TypeScript プロジェクトになっていました。

開発サーバーを立ち上げると、ブログサイトが立ち上がります!
Blog Starter Kitのトップ画面
Blog Starter Kit の初期トップ画面

ディレクトリ構造を見てみるとトップはsrc/app/page.tsxが表示されているようです。

詳細ページはsrc/app/posts/[slug]/page.tsxに実装があります。
開いてみるとパスがposts/hello-worldのようになっており、[slug]はプロジェクトルートの_posts/下にある markdown のファイル名になっていることがわかります。
Blog Starter Kitのトップ画面
Blog Starter Kit のポスト詳細画面

app 下のディレクトリ構造がルーティングになり、その中のpage.tsxがコンテンツになるという規約。
これが App Router か...

いきなり小問題発生

vscode で開発しているのですが、ローカルサーバーが立ち上がるものの型エラーのウニョウニョが出続ける症状が発生します。
原因は yarn の v4 系を使ったことでした。

VSCodeのエディタでエラーが出ている様子
VSCode のエディタでエラーが出ている様子

yarn の v2 以降は Plug'n'Play(pnp)と呼ばれる仕組みがデフォルトで利用されるようです。
yarn installを実行してもnode_modulesが生成されず、.pnp.cjsというファイルが生成されます。

vscode はデフォルトでnode_modulesを参照して型の情報があるかを判断しますが、PnP では.pnp.cjsという単一ファイルで依存関係の解決を行うので、動くけどエディタ上はエラーになるというわけです。

yarn のチームが案内している次のコマンドを実行することで解決しました。

yarn dlx @yarnpkg/sdks vscode

この辺り完全なキャッチアップはできていませんが、以下が参考になりそうです。

markdown のスタイリング

Blog Starter Kit の初期状態は markdown を HTML 文字列にパースして表示する仕組みがすでにできています。
しかし、スタイリングは最低限のためコードを書いたり、コメントのようなことをしたりはできません。

本来ならせっせと CSS を書くところですが、ありがたいことに Zenn で利用されている markdown パーサーや CSS が公開されています。

zenn-editor

今回はひとまずスタイリングをなんとかしたいので、次の 2 つをインストールします。

  • zenn-markdown-html
  • zenn-content-css

インストールできたらまずはパーサーを変更します。

markdownToHtml.ts
- import { remark } from "remark";
- import html from "remark-html";
+ import m2h from "zenn-markdown-html";

export default async function markdownToHtml(markdown: string) {
-  const result = await remark().use(html).process(markdown);
-  return result.toString();
+  return m2h(markdown);
}

次にスタイルを当てていきます。
最初はposts/[slug]/page.tsx<article>タグに当てていたのですが、タイトルや日付など markdown 本文で記載した内容以外にもスタイルが反映されて予期せぬ表示になることがありました。

パースしたコンテンツを表示するコンポーネントだけに影響するようにするのが良さそうです。

_components/post-body.tsx
import "zenn-content-css"

type Props = {
  content: string
}

export function PostBody({ content }: Props) {
  return (
    <div className="mx-auto mt-16 znc">
      <div
        dangerouslySetInnerHTML={{ __html: content }}
      />
    </div>
  )
}

これで以下のような記述にも対応できるようになりました!

:::message
メッセージです!
:::

まとめ

今回は Next.js の App Router で動作するブログプロジェクトを立ち上げ、Zenn と同じスタイリングをするところまでまとめました。

App Router の動作は直感的で個人的にはすぐに馴染みました。

次回はいきなりブログ本体関係ないですが気になっていた Biome の導入をまとめたいと思います。