RSS and Sitemap
The blog template ships with both an RSS feed and a sitemap pre-configured. This page explains how they work and how to customize them.
Sitemap
The sitemap is generated by @astrojs/sitemap. It automatically includes every statically generated page.
Setup
Install the integration (already included in the blog template):
pnpm add @astrojs/sitemap
Add to astro.config.mjs:
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://your-site.com", // Required for sitemap
integrations: [
notro(),
sitemap(),
],
});
The sitemap is generated at /sitemap-index.xml and linked from the <head> automatically.
Excluding pages
To exclude specific pages from the sitemap:
sitemap({
filter: (page) => !page.includes("/draft/"),
}),
RSS feed
The RSS feed is served from /rss.xml and is generated by @astrojs/rss.
Setup
Install the package (already included in the blog template):
pnpm add @astrojs/rss
Create src/pages/rss.xml.ts:
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
import { getSortedPosts, excludeFixedPages } from "@/lib/posts";
import { config } from "@/config";
import type { APIContext } from "astro";
export async function GET(context: APIContext) {
const allPosts = await getCollection("posts");
const posts = getSortedPosts(excludeFixedPages(allPosts));
return rss({
title: config.site.name,
description: config.site.description,
site: context.site!,
items: posts.map((post) => ({
title: post.data.title,
description: post.data.description,
pubDate: post.data.date ? new Date(post.data.date) : new Date(),
link: `/blog/${post.data.slug}/`,
})),
customData: `<language>ja</language>`,
});
}
Linking the feed
Add the RSS feed link to your <head> in Layout.astro:
<link
rel="alternate"
type="application/rss+xml"
title={config.site.name}
href={new URL("rss.xml", Astro.site)}
/>
Including content in the feed
To include the full HTML content of each post in the RSS feed, use sanitizeHtml and marked:
pnpm add sanitize-html marked
import sanitizeHtml from "sanitize-html";
import { marked } from "marked";
items: posts.map((post) => ({
title: post.data.title,
pubDate: post.data.date ? new Date(post.data.date) : new Date(),
link: `/blog/${post.data.slug}/`,
content: sanitizeHtml(await marked.parse(post.data.markdown)),
})),
Note: The RSS content is rendered from raw markdown, not from the compiled MDX with Notion components. Custom block types (callouts, toggles, etc.) will appear as plain text in feed readers.
robots.txt
Create public/robots.txt to control crawler access:
User-agent: *
Allow: /
Sitemap: https://your-site.com/sitemap-index.xml
Open Graph and SEO
The blog template's Layout.astro includes Open Graph meta tags automatically:
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={Astro.url} />
<meta property="og:type" content="article" />
Set site in astro.config.mjs to ensure og:url is an absolute URL.
Per-post OG image
To add per-post Open Graph images, generate them using Astro's @vercel/og or satori:
pnpm add @vercel/og
Create src/pages/og/[slug].png.ts and pass the resulting URL as og:image in your layout. Refer to the Astro docs for details on dynamic image generation.