こういう状況がたまにあるはず。
.astro
でリスト表示をする- そして各アイテムにサムネイルを出す
そんな時にどうするか。
MDXの場合
その各アイテムがContent collectionsになってて、ソースがMDXの場合。
この場合は、MDXからサムネをexportすればよくて、特に困らない。
Images | Docs https://docs.astro.build/en/guides/images/#images-in-content-collections
schema
でも縛れる。
const blogCollection = defineCollection({
schema: ({ image }) =>
z.object({
title: z.string(),
cover: image(), // 👀
coverAlt: z.string(),
}),
});
そうじゃない場合は?
ドキュメントを読んでてそう思ってしまった。
Content collectionsでも、カスタムloaderを使ってるとか、CSVやJSONから生成してるとかの場合に、どうすれば・・・?
まず最初に思いつくのは、/public
に置くやり方。
でもこれは避けたい。パスの扱いも困るし、なによりカオスになるのが目に見えてるから。
となると、なんとかしてAstro or Viteに画像を認識させて、そのパスを管理させる必要がある。
ということに悩んでたら、いちおうドキュメントにレシピとして書いてあった。
Dynamically import images | Docs https://docs.astro.build/en/recipes/dynamically-importing-images/
こうする
export async function getItemThumbnailSrcById(id: string) {
const path = `/path/to/data/${id}/thumb.png`;
const img = import.meta.glob<{ default: ImageMetadata }>(
// NOTE: This must be a static string literal path
"/path/to/data/*/thumb.png",
)[path];
if (!img) throw new Error(`Failed to load ${path}`);
return img();
}
import.meta.glob()
でViteに認識させて、画像のパスを取得する関数にしておく。
あとはこれを.astro
で使うだけ。
<Image src={getItemThumbnailSrcById(item.id)} alt="" />
ImageMetadata
という型により、この関数の返り値はPromise<{ default: ImageMetadata }>
になる。
astro:assets
のImage
コンポーネントのsrc
は、ImageMetadata
だけでなく、Promise<{ default: ImageMetadata }>
も受け付けてくれるので、リストでも気にせず使えるというわけ。
ただのimg
の場合はこうはいかない。どうしてもimg
を使う場合は、import.meta.glob()
で{ eager: true, import: "default" }
などを駆使して頑張るしかない。
Imports reference | Docs https://docs.astro.build/en/guides/imports/#importmetaglob
Features | Vite https://vite.dev/guide/features.html#glob-import
Content collectionsで画像でもPDFでもなんでも管理できるようになったらいいのに。