Astro MDX integrationで画像をレスポンシブ対応にする方法
自転車ブログをGatsbyからAstroに書き換えた。
新興フレームワークだけあり、Gatsbyのように豊富なプラグインによる便利な設定というものはないので、殆どの機能を自分で実装する必要がある。
Head APIがなく、レイアウトコンポーネントから必要な要素を全てPropsで渡してhead
タグに記述したり、クライアント処理をAstroでやるのはつらいのでReactを使ったりと、Gatsbyに慣れているといろいろ面倒なところが多く感じた。
その中でも、ナレッジが少なく(というかundocumented)でデフォルト処理してくれない、MDX内に相対パスで記述した画像をレスポンシブ化する部分だけ抜粋してメモ。
記事の目的:MDXの画像もContent Collectionで管理したい
AstroにはContent Collectionという機能があり、src
ディレクトリ内に配置された画像を自動的にフォーマット変換・レスポンシブ対応で自動的に複数サイズの画像をsharpでレンダリングする機能がある。
.astro
コンポーネントの場合は、組み込みの<Image>
, <Picture>
タグを通じてこの最適化をコントロールできる。
一方で、このContent Collectionは.md
, .mdx
ファイル内の画像リンクも最適化してくれるのだが、この最適化は最適化と名ばかりのオリジナルサイズのwebpを吐き出すだけである(しかもフォールバックもない)。
Content Collectionには(ドキュメントを読む限り)グローバル設定が存在しないうえ、gatsby-plugin-image
のようなキャプション設定機能も存在していないため、自前でこれらを実装する必要がある。
Astroの書き味はNext.jsに似ていて、シンプルなコア機能やコンポーネントを提供する方針のようだ。
記事作成時の環境
- Astro.3.x
- Content Collection
-
@astro/mdx
integration >1.1.0
コード実例
実は、@astro/mdx
インテグレーションにはCustom Componentのマッピングでimg
を指定した場合、ContentCollectionによって処理された画像に限りAstroのImageMetadata
タイプがPropsとして渡される仕様になっている。
これを知っているとシンプルに記述できる。この情報にたどり着くまでは自分でrehype, remarkプラグインを書かねばならないのかと絶望していたところ。
---
//MdxPicture.astro
import type { ImageMetadata } from "astro"
import { Picture } from "astro:assets"
type Props = {
src: string | ImageMetadata
alt: string
}
const { src, alt } = Astro.props
---
{
typeof src === "string" ? (
<figure>
<img class="mx-auto" src={src} alt={alt} />
<figcaption class="not-prose text-center text-xs text-secondary md:text-sm">
{alt}
</figcaption>
</figure>
) : (
<figure>
<Picture
formats={["webp"]}
fallbackFormat="jpg"
widths={[360, 752]}
class="mx-auto"
{src}
{alt}
/>
<figcaption class="not-prose text-center text-xs text-secondary md:text-sm">
{alt}
</figcaption>
</figure>
)
}
//[...slug].astro
---
~
~
import { MdxPicture } from "~~~"
~
~
const { Content } = await post.render()
---
<Content
components={{
~
~
img: MdxPicture
}}
/>
componentsとして渡す際、特段Propsとして明示的に渡さないところがポイント。
ドキュメントに書く価値のある情報のはずなのだが…。
Discussion