home

ブログをSSGにした

Notionで書いた記事をマークダウンにしてNext.jsで表示させている静的サイト(これ)がSSRである必要がないと思ったのでSSGに変更してみる。

※Next.js14.2.3 AppRouter で実装しているので注意。

ネットにはgetStaticPropsgetStaticPathなどの情報がありそれを読みながら実装していたが何回やってもエラーになり詰まってしまったがよくよく調べるとgetStaticPropsgetStaticPathはAppRouterでは対応していないっぽい….(前のバージョン?PageRouter?なら使用できるっぽい)

そこで改めてドキュメントを読んで実装してみたら簡単に実装できて嬉しかった。

自分は動的ルーティング(dynamic rooting)をSSGしたかったのでそれについてメモしていく。

動的ルーティングページをgenerateStaticParamsを使ってSSGする

もともとは以下のコードの通り記述していた。

// app/post/[slug]/page.tsx

export default function Page({ params }: { params: { slug: string }}) {
	// ページに表示する.mdxファイルを取得
  const fullPath = path.join(process.cwd(), 'src', 'static', 'markdown', `${params.slug}.mdx`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const { content } = matter(fileContents);
  return <CustomMDX source={content} />
}

ただ、これだとSSRになることがわかった。

なぜなら動的ルーティングは slug が動的に決まるのでslug が何者かが分かってからじゃないとレンダリングできない。よってSSRになってしまう。

なので予め受け取る可能性のある slug を定義しておけば、その slug を使用してページをビルドしてくれるので結果SSGになるみたい。

受け取る可能性のある slug を定義する方法は以下。

export async function generateStaticParams() {
  const directoryPath = path.join(process.cwd(), 'src', 'static', 'markdown');
  const metadata = getMarkdownMetadata(directoryPath);
  const paths = metadata.map(data => ({ slug: data.slug }));
  console.log(paths);
  return paths;
}

// console.log output
// { slug: 'post1_react_doc' },
// { slug: 'post2_gw' },
// { slug: 'post3_mobile_text_scale' },
// { slug: 'post4_html_to_mdx' }

このように return paths; で受け取る可能性のある slug を返せばOK。これでNext.jsは paths の値を使ってbuildしてくれるはず。

これで動的ルーティングを静的にできたので以下のように Page() の上に記述すればSSGになる。

// app/post/[slug]/page.tsx

export async function generateStaticParams() {
  const directoryPath = path.join(process.cwd(), 'src', 'static', 'markdown');
  const metadata = getMarkdownMetadata(directoryPath);
  const paths = metadata.map(data => ({ slug: data.slug }));
  return paths;
}

export default function Page({ params }: { params: { slug: string }}) {
  const fullPath = path.join(process.cwd(), 'src', 'static', 'markdown', `${params.slug}.mdx`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');
  const { content } = matter(fileContents);
  return <CustomMDX source={content} />
}

// npm run build => log

Route (app)                              Size     First Load JS
┌ ○ /                                    621 B          99.6 kB
├ ○ /_not-found                          875 B          87.9 kB
├ ○ /icon.svg                            0 B                0 B
└ ● /post/[slug]                         292 B          92.4 kB
    ├ /post/post1_react_doc
    ├ /post/post2_gw
    ├ /post/post3_mobile_text_scale
    └ /post/post4_html_to_mdx
+ First Load JS shared by all            87 kB
  ├ chunks/23-6de92bf77c16c86b.js        31.5 kB
  ├ chunks/fd9d1056-62aaf4b921c84028.js  53.7 kB
  └ other shared chunks (total)          1.89 kB

○  (Static)  prerendered as static content
●  (SSG)     prerendered as static HTML (uses getStaticProps)

表示速度が格段を上がって快適。SSGいい。

他にも定義していないページにアクセスした時のハンドリングができる dynamicParams など、ドキュメントにいろいろ書いてあるので暇な時見てみる。(Next.jsDoc-generate-static-params

Monkey Logo

konpay.eth

福井県出身のエンジニアです。31歳です。田舎からフロントエンドを開発しています。TpeScript, React をよく書きます。バックエンドは Node.js をお遊び程度で書いています。将来的には TypeScript と Node.js でフルスタック開発をしたいです。仮想通貨や NFT など Web3.0 領域が好きです。