Astro RSS Feed Blog Post Images

Guide on adding post main images to your rss feed for use in MailerLite and other tools.

Cover image for Astro RSS Feed Blog Post Images
Web Reaper avatar
Web Reaper

5 min read


Want to add an image to your RSS feed blog posts to integrate with something like MailerLite? Here I’ll go through how to do this on an Astro site, although similar concepts will apply for any rss feed. You can do this using the integration @astrojs/rss. the Astro RSS docs can be found here.

General @astrojs/rss setup

npm install @astrojs/rss

Create a file in src/pages with a name of your choice and the extension .xml.ts to be used as the output URL for your feed. A common name is rss.xml. When your website builds, your rss feed will exist at site/rss.xml.

In this new file, import the rss() helper from @astrojs/rss and export a function that returns the xml data.

Note that context.site is defined in astro.config.mjs and is something like “webreaper.dev”.

import rss from "@astrojs/rss";

export async function get(context) {
  return rss({
    // `<title>` field in output xml
    title: "Web Reaper Blog",
    // `<description>` field in output xml
    description: `
      Quick reference for web development tips, tools, and techniques.`,
    // Pull in your project "site" from the endpoint context
    // https://docs.astro.build/en/reference/api-reference/#contextsite
    site: context.site,
    // Array of `<item>`s in output xml
    // in "items" is where we will put blog post data
    items: [],
  });
}

Blog post items

Lets say we have a blog post content collection called “posts”. We can get the posts data and add them to the rss items like below.

CAUTION

The specific post.data.item could change based on how you defined your frontmatter.

import rss from "@astrojs/rss";
import { getCollection, CollectionEntry } from "astro:content";

export async function get(context) {
  const posts: CollectionEntry<"posts">[] = await getCollection("posts");

  return rss({
    // `<title>` field in output xml
    title: "Web Reaper Blog",
    // `<description>` field in output xml
    description: `
      Quick reference for web development tips, tools, and techniques.`,
    // Pull in your project "site" from the endpoint context
    // https://docs.astro.build/en/reference/api-reference/#contextsite
    site: context.site,
    // Array of `<item>`s in output xml
    // in "items" is where we will put blog post data
    items: posts.map((post) => ({
      title: post.data.title,
      pubDate: post.data.pubDate,
      description: posts.data.description,
      author: `developer@webreaper.dev (${post.data.author})`,
      // This example assumes all posts are rendered as `/posts/[slug]` routes
      link: `/posts/${post.slug}/`,
    })),
  });
}

In order to add image support, we need to add an xml namespace or “xmlns”. If you are not familiar with this, it is basically a definition of specific tags made by someone else. Further information can be found here.

The namespace we will be adding is “media” from http://search.yahoo.com/mrss/. Then we can use that namespace to define our image.

import rss from "@astrojs/rss";
import { getCollection, CollectionEntry } from "astro:content";

export async function get(context) {
  const posts: CollectionEntry<"posts">[] = await getCollection("posts");

  return rss({
    // `<title>` field in output xml
    title: "Web Reaper Blog",
    // `<description>` field in output xml
    description: `
      Quick reference for web development tips, tools, and techniques.`,
    // Pull in your project "site" from the endpoint context
    // https://docs.astro.build/en/reference/api-reference/#contextsite
    site: context.site,
    // add `xmlns:media="http://search.yahoo.com/mrss/"`
    xmlns: {
      media: "http://search.yahoo.com/mrss/",
    },
    // Array of `<item>`s in output xml
    // in "items" is where we will put blog post data
    items: posts.map((post) => ({
      title: post.data.title,
      pubDate: post.data.pubDate,
      description: posts.data.description,
      author: `developer@webreaper.dev (${post.data.author})`,
      // This example assumes all posts are rendered as `/posts/[slug]` routes
      link: `/posts/${post.slug}/`,

      // custom data for media. The url must be the full url (including https://)
      customData: `<media:content
          type="image/${post.data.heroImage.format == "jpg" ? "jpeg" : "png"}"
          width="${post.data.heroImage.width}"
          height="${post.data.heroImage.height}"
          medium="image"
          url="${context.site + post.data.heroImage.src}" />
      `,
    })),
  });
}

Starwind UI

starwind ui

I've learned a lot developing with Astro. So I crafted 37+ animated and accessible components to help you get started with your next project. Inspired by shadcn/ui with seamless CLI installation. Free and open source.

Verify your RSS feed

A few resources to verify your RSS Feed is setup correct (or help with debugging) are below. Simply put your RSS feed address into the website and it will tell you what is wrong or suggest improvements.

Use with MailerLite, Mailchimp, etc

Now when you create campaigns in MailerLite, you can have it use your RSS feed and your image, title, description, publish date, and author will all show up. Your automated marketing has been leveled up.

Bonus: Atom support

When verifying your RSS feed, you’ll probably see a recommendation for atom support. You can do this with the following few additions.

import rss from "@astrojs/rss";
import { getCollection, CollectionEntry } from "astro:content";

export async function get(context) {
  const posts: CollectionEntry<"posts">[] = await getCollection("posts");

  return rss({
    // `<title>` field in output xml
    title: "Web Reaper Blog",
    // `<description>` field in output xml
    description: `
      Quick reference for web development tips, tools, and techniques.`,
    // Pull in your project "site" from the endpoint context
    // https://docs.astro.build/en/reference/api-reference/#contextsite
    site: context.site,
    // add `xmlns:media="http://search.yahoo.com/mrss/"`
    xmlns: {
      media: "http://search.yahoo.com/mrss/",
      atom: "http://www.w3.org/2005/Atom",
    },
    // add atom:link to be compatible with atom
    customData: `<atom:link href="${context.site}rss.xml" rel="self" type="application/rss+xml" />`,
    // Array of `<item>`s in output xml
    // in "items" is where we will put blog post data
    items: posts.map((post) => ({
      title: post.data.title,
      pubDate: post.data.pubDate,
      description: posts.data.description,
      author: `developer@webreaper.dev (${post.data.author})`,
      // This example assumes all posts are rendered as `/posts/[slug]` routes
      link: `/posts/${post.slug}/`,

      // custom data for media. The url must be the full url (including https://)
      customData: `<media:content
          type="image/${post.data.heroImage.format == "jpg" ? "jpeg" : "png"}"
          width="${post.data.heroImage.width}"
          height="${post.data.heroImage.height}"
          medium="image"
          url="${context.site + post.data.heroImage.src}" />
      `,
    })),
  });
}

Great Success

You now have blog post images in your RSS feed which work nicely with automated emails to your subscribers. Great success. 🚀

Don't forget to follow me on Twitter for more Astro, web development, and web design tips!


Share this post!