AstroJS introduced a feature in v5.10 that allows automatic generation of responsive images via the <Image> and <Picture> components. This is a very convenient features, since responsive images—i.e. images that are delivered in various sizes depending on device viewport size—are generally a SEO best practice that will improve your page rank with various search engines.
Responsive images are unlocked by specifing the layout property—or, alternatively a combination of widths and sizes properties or the densities property—in either <Image> or <Picture> components. For example,
---
import { Image } from 'astro:assets';
import myImage from '../assets/my_image.png';
---
<Image src={myImage} alt="This will use full-width layout" layout="full-width" />
Astro will generate variants of the given image asset in various sizes and specify their respective URLs in the srcset attribute of the generated HTML <img> element.
The documentation, unfortunately, does not clearly state that in order to create image variants, the image asset must be imported in the Astro component (as shown in the line import myImage from '../assets/my_image.png' of the code snippet above). I.e. you won’t be able to pass an image URL.
Now, what if we want dynamically generate responsive images from asset URLs like, for example, image URLs specified in the Markdown frontmatter of a blog post as shown below?
---
title: my post
image: /src/assets/images/my-image.png
imageAlt: a beautiful image
---
the post content
One way is to use AstroJS’ content collection feature and, more specifically, the schema definition. For the example post shown above, we may define a schema—in src/content.config.ts—and assign it to the collection called blog as follows:
import { defineCollection, z } from "astro:content";
const blogCollection = defineCollection({
schema: ({ image }) => z.object({
title: z.string(),
image: image(),
imageAlt: z.string(),
}),
});
export const collections = {
blog: blogCollection,
};
Note that the image property of the schema object is a call to a function image. This is the image helper utility which will automatically import the image for the given URL if it exists.
Having done that, the imported image will now be accessible in the entries of the content collection returned by getCollection("blog")—accessible from the post.data property—and we can simply pass it to our layout or image components.
---
import { Image } from "astro:assets";
import { getCollection } from "astro:content";
const allBlogPosts = await getCollection("blog");
---
{
allBlogPosts.map((post) => (
<div>
<Image src={post.data.image alt={post.data.imageAlt} />
<h2>
<a href={"/blog/" + post.slug}>{post.data.title}</a>
</h2>
</div>
))
}