Astro と React のコンポーネントでブログを作り直した

クラスメソッドに入社して以来 DeveloperIO にブログを書くようになったため、このブログに記事を書くことがなくなりましたが、最近 Astro が 1.0 になったので作り直してみることにしました。

またこの記事は Astro の機能を網羅的に紹介するものではなく、作成時において気になった点を記録しておくものです。そのため Astro がどういったものかを知るために、まずは公式のドキュメントを参照ください。

前提

もともとは Metalsmith で作成していたものを、Next.js が SSG に対応したとき書き換えたものでした。

そのため記事のデータは Web の API で取得するようなものではなく、ローカルの Markdown のファイルからビルドするという形をとっています。それを Next.js でやろうとすると、意外と自分で結構な量のコードを書かないといけないのでメンテナンスが面倒です。

Astro であれば、page ディレクトリ内に .md または .mdx ファイルを置くだけで自動的にページのルートが構築されるため、これがもっとも大きな移行動機になります。

ドキュメントを見ながら進めれば困ることはありません。今回は pnpm を使いましたが特にトラブルもありませんでした。

ただ私が作成したときには日本語の翻訳が英語バージョンよりもやや古いバージョンを参照しているようで、古い情報のままになってしまっていた箇所があったため、その点は少し気をつけたほうが良さそうです。

pnpm create astro@latest

テンプレートに Just the basics (recommended) を選択すると、.vscode も合わせて生成されます。そのため VSCode であれば特に意識しなくとも開発環境が整います。とはいえ今のところ、Astro コンポーネントの開発者体験がいいかというとそこまででもない印象です。

Astro コンポーネント

---
// Component Script (JavaScript)
---

<!-- Component Template (HTML + JS Expressions) -->

frontmatter のようにコードフェンスの --- で括られたエリアを Component Script、HTML やコンポーネントを記述する場所を Component Template と呼ばれています。

Component Script

Component Script には JavaScript あるいは TypeScript のコードを記述することで、Component Template で使用する値を取得・生成できます。

AstroでSSGする場合の個人的ベストプラクティス - console.lealog();
個人的なやつなので、すべての案件にハマるわけではないはず。今回はたまたまAstroを選んでるけど、他のものを使ってSSGする場合にも、ある程度は当てはまる内容かと。
AstroでSSGする場合の個人的ベストプラクティス - console.lealog(); favicon https://lealog.hateblo.jp/entry/2022/06/13/143211
AstroでSSGする場合の個人的ベストプラクティス - console.lealog();

上記のサイトにもあるように、あまりコードをベタベタと書かないようにすることはよい手段と感じます。テストも書きやすいですしね。

Component Template

Component Template では JSX と異なり classclassName にしたり、kebab-case の属性名を camelCase にしたりする必要はありません。vue.js や svelte のようなにスロット、あるいは名前付きのスロットを使うこともできます。

Layout は次のようにしました。

  • BaseLayout.astro - 必ず使用する Layout
    • BlogPostLayout.astro - Markdown で使用する Layout

BaseLayout.astro ではほとんどなにもせず、SEO 用のデータを簡単に生成できるようにしてあります。すべてのページで使用するものになるので、あまり多くのことはしないようにしています。

Blog 用に使用するレイアウトは必須なので BlogPostLayout.astro を作成しています。これは内部で BaseLayout.astro を使っています。

CSS

@astrojs/tailwind も提供されているのですが、今回は Tailwind を使いませんでした。

結論から言うと、Astro では Tailwind のようなユーティリティクラスを使うことで開発はしやすくなるように感じています。

ユーティリティクラスを使用しなかった場合に問題となるのは normarize.css のようなものを導入するケースです。Astro コンポーネントが :where を使用してスコープを作るため詳細度が低く、normarize.css のスタイルを上書きするためには毎回 :global が必要になります。これが意外と面倒で、これだけでも個人的には Tailwind の導入動機になり得ます。

Markdown

*.md/page/** に配置して、frontmatter に使用する Layout を指定するだけです。Next.js で書いたコードのことを考えるととても楽です。デフォルトでコードにはシンタックスハイライトもつけてくれます。

プラグインを追加したくなったら astro.config.ts に追加するだけです。そのときにデフォルトで導入されているプラグインをそのまま使用する場合には extendDefaultPlugins: true にしておく必要があります。

私は使わないのですが、MDX も @astrojs/mdx の追加で使用できます。

React

Astro は React のコンポーネントも呼び出せて、次のコマンドだけで導入できます。

pnpm astro add react

今回はもともとが AMP の静的なサイトの移植で、JavaScript が不要になるため普通に呼び出しただけです。

Astro ですごいところは単に React のコンポーネントを使えるからというわけではありません。

それは今回使用しなかったのですが client:* ディレクティブで、これはコンポーネントのハイドレーションを制御できます。詳しくはドキュメントを参照してください。

また可搬性を考慮し Astro コンポーネントではなく、あえて React コンポーネントを新規で作成することがあります。その場合には Astro のコンポーネントを含めることができません。可搬性を考慮したのだからそれはそうなのですが、@astrojs/image<Image /><Picture /> を使えないので、そこで葛藤を抱えることがありました。

RSS / Sitemap

Astro Integrations を使えば Blog にほしい機能は簡単に追加ができます。

sitemap はデフォルトだとそこまでの容量にならなくても分割され、sitemap.xml ではなく sitemap-index.xml を生成する点に注意が必要です。

ただし RSS は少し使いにくく、自分で素朴に import.meta.glob を使って生成するコードを追加して @astrojs/rss は使いませんでした。具体的に使いにくかったのは frontmatter で投稿日として pubDate が必須になっていることです。