ionicons-v5-a Back
Assigning custom components to HTML elements in Astro collections - Image with figcaption example
astro

3 min read

Assigning custom components to HTML elements in Astro collections - Image with figcaption example

Astro collections allow you to keep your content type-safe and provide a built-in mechanism to render your MDX files. Let's consider an example of how to extend the default components mapping for an img tag with a figcaption.


What is Astro collections?

Processing and managing all MDX files can be a challenge, especially when introducing new properties in the frontmatter over time. Astro collections enable you to create type-safe Astro code. When I built my publication with Next.js, I used a Contentlayer for a similar purpose. However, it was surprising to discover that Astro has this mechanism built-in.

Content collections are the best way to manage and author content in any Astro project. Collections help to organize your documents, validate your frontmatter, and provide automatic TypeScript type-safety for all of your content.

Astro collections are very useful when you have different types of content within the same publication, each with its own frontmatter, for example.

Astro collections has a pretty neat documentation so let’s focus on our particular use case.

The problem

Let’s consider the code assuming we use it for our blog post astro template

// src/pages/blog/[slug].astro
 
---
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import ArticleImage from '../../components/ArticleImage.astro';
 
interface Props {
  post: CollectionEntry<'blog'>;
}
 
export async function getStaticPaths() {
  const blogPosts = await getCollection('blog');
 
  return blogPosts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
  }));
}
 
const { post } = Astro.props;
const { Content, headings } = await post.render();
//      ^^^^^^^
//      will be used to render html of our article (it is the place where mdx -> html conversion happens)
---
<article>
    <Content />
</article>

In this example md version of image

![some image](./some-image.png)

will be converted to

<img src="./some-image.png" alt="some image" />

Which is actually not so good in case we want to enrich our image with other elements, in my case it was a caption.

The solution

Lucky me Astro has solution for this, so we can define a custom component for all default elements.

<Content components={{ img: ArticleImage }} />
// ArticleImage.astro
---
import type { ImageMetadata } from 'astro';
import { Image } from 'astro:assets';
 
type Props = {
  src: string | ImageMetadata;
  // src will be ImageMetadata type in case you use an image from your content and string in case you use some external image
  alt: string;
};
 
const { src, alt } = Astro.props;
---
 
<figure>
  {
    typeof src === 'string' ? (
      <img src={src} alt={alt} class="rounded-3xl" />
    ) : (
      <Image {src} {alt} class="rounded-3xl" />
    )
  }
  {alt && <figcaption class="text-center">{alt}</figcaption>}
</figure>

Dzmitry Kozhukh

Written by Dzmitry Kozhukh

Frontend developer


Next