# Add a blog and an RSS feed to your vagejs site

> Set up a blog in vagejs with posts that list themselves, a latest-posts box on the home page and an RSS feed, with the exact config and real output.

A new vagejs site already has a blog: each Markdown file in `src/pages/blog/` is a post, listed newest first. Add one `seo.feeds` line to `vage.config.ts` for an RSS feed, and new posts appear on the blog index, the home page and in the feed without anyone editing a list.

4 November 2026 · 6 min read · by [Venkatesh A G E](#author)

A blog sounds simple until you maintain one. The index page needs updating every time you post. The home page's "latest posts" box shows last spring's news. The RSS feed, if there is one, came from a plugin nobody remembers installing. In this walkthrough we'll set up a blog in vagejs where new posts appear everywhere on their own: on the blog index, on the home page and in an RSS feed.

We built this exact setup with vagejs 0.3.0 for a made-up bakery. The commands, files and output below come from that test project.

## Does a new vagejs site already have a blog?

A new vagejs site already has a blog. `npm create vagejs@latest` gives you `src/pages/blog/index.md` (the blog's front page) and a layout, `src/layouts/blog.ts`, that lists every page under `/blog/`, newest first. So the first step is simply writing a post.

terminal

```
npm create vagejs@latest corner-bakery
cd corner-bakery
npm install
npm run dev
```

## Step 1: Write a post

Each post is a Markdown file in `src/pages/blog/`. The file name becomes the address, so `src/pages/blog/sourdough-starter.md` is published at `/blog/sourdough-starter/`.

```
---
title: How we keep our sourdough starter alive
description: The feeding schedule, flour and kitchen temperature we use to keep a ten-year-old sourdough starter healthy.
type: article
author: Priya Nair
datePublished: 2026-09-02
image: /images/starter.png
---

# How we keep our sourdough starter alive

Twice a day, every day.
```

What each field does:

-   **`title`** and **`description`** appear in search results, social previews, the blog index and the feed.
-   **`datePublished`** decides the order. Posts without a date go to the end of the list.
-   **`type: article`**, **`author`** and **`image`** give the post Article structured data, which helps search engines understand it's a dated article by a named person. Only use a real author and a real date.

Save, and the post shows up on `/blog/` in the dev server. You never edit a list of posts by hand. The [content authoring guide](https://vagejs.com/docs/content-authoring/) lists every frontmatter field.

## Step 2: Turn on the RSS feed

RSS is a simple, decades-old format that lets people follow your blog in a feed reader without signing up for anything. In vagejs it's one line in `vage.config.ts`:

TypeScript

```
import { defineConfig } from "vagejs";

export default defineConfig({
  site: "https://www.example-bakery.net",
  title: "Corner Bakery",
  description: "Bread, pastries and notes from a small neighbourhood bakery.",
  seo: {
    feeds: [{ under: "/blog/", title: "Corner Bakery journal", limit: 10 }],
  },
  security: { outboundLinks: "require" },
});
```

`under` is the section whose pages go in the feed. `title` is optional; without it, the feed uses your site's title. `limit` is how many recent posts to include; the default is 20.

The `site` line matters here. A feed needs full addresses, so without `site` vagejs skips it and tells you why:

```
Warning: seo.feeds: the feed for /blog/ needs "site" in vage.config.ts and was skipped.
```

With `site` set, `npm run build` lists the feed among the files it writes:

```
  /                              -> index.html
  /about/                        -> about/index.html
  /blog/                         -> blog/index.html
  /blog/autumn-menu/             -> blog/autumn-menu/index.html
  /blog/sourdough-starter/       -> blog/sourdough-starter/index.html
                                 -> robots.txt
                                 -> sitemap.xml
                                 -> blog/feed.xml
                                 -> llms.txt

Built 5 page(s) into ./dist in 406ms
```

## What's in the feed?

Here's the start of the `blog/feed.xml` our test build produced, with the newer of the two posts:

```
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Corner Bakery journal</title>
    <link>https://www.example-bakery.net/blog/</link>
    <description>Posts from this site, newest first.</description>
    <language>en</language>
    <atom:link href="https://www.example-bakery.net/blog/feed.xml" rel="self" type="application/rss+xml" />
    <lastBuildDate>Thu, 01 Oct 2026 00:00:00 GMT</lastBuildDate>
    <item>
      <title>Our autumn menu is here</title>
      <link>https://www.example-bakery.net/blog/autumn-menu/</link>
      <guid isPermaLink="true">https://www.example-bakery.net/blog/autumn-menu/</guid>
      <description>Pumpkin loaves, apple turnovers and the return of the cardamom bun, from the first week of October.</description>
      <pubDate>Thu, 01 Oct 2026 00:00:00 GMT</pubDate>
    </item>
```

The channel description came from the blog index page's own `description`, since we didn't set one in the config. Each item takes its title and summary from the post's frontmatter, and its date from `datePublished`. The feed's dates come only from frontmatter, so building twice gives the same file.

![Diagram showing a post's title, description, datePublished and address mapping to the feed item's title, description, pubDate and link, with a note that drafts and noindex pages are left out and the full text is not included](https://vagejs.com/images/blog/add-blog-rss-feed-vagejs-item.svg)

vagejs also adds a discovery link to the `<head>` of the blog index and every post, so browsers and feed readers can find the feed from any post's address:

HTML

```
<link rel="alternate" type="application/rss+xml" title="Corner Bakery journal" href="https://www.example-bakery.net/blog/feed.xml" />
```

Pages outside `/blog/`, such as your home page, don't get that link. If you want a visible "Follow with RSS" link elsewhere, add it yourself, as we do next.

## Step 3: Show the latest posts on the home page

Create `src/layouts/home.ts`:

TypeScript

```
import { collection, html, raw, type Layout } from "vagejs";
import defaultLayout from "./default.ts";

// Home page: the page's own text, then the three newest posts.
const layout: Layout = (props) => {
  const latest = collection(props.pages, { under: "/blog/", sort: "date", limit: 3 });
  const list = html`<h2>Latest posts</h2>
<ul>${latest.map((p) => html`<li><a href="${p.route}">${p.title}</a> <time datetime="${p.frontmatter.datePublished}">${p.frontmatter.datePublished}</time></li>`)}</ul>
<p><a href="/blog/feed.xml">Follow with RSS</a></p>`;
  return defaultLayout({ ...props, content: String(html`${raw(props.content)}${list}`) });
};

export default layout;
```

Then add `layout: home` to the frontmatter of `src/pages/index.md`. `collection()` picks the pages under `/blog/`, sorts them newest first and keeps three. It leaves out the blog index page itself, so you only get posts. The layout reuses the starter's `default.ts` for the header and styles. [Templates and layouts](https://vagejs.com/docs/templates-and-layouts/) explains `html`, `raw` and `collection()` in more detail.

Our check after these changes:

```
Checked 6 page(s): 0 error(s), 0 warning(s).
Passing checks do not guarantee indexing, rankings, rich results or AI citations.
```

## Step 4: Write ahead with drafts

Add `draft: true` to a post's frontmatter and it's left out of the build, the blog index, the home page list and the feed. It still shows in `npm run dev`, so you can preview it.

The check has your back if you link to a draft by mistake. We linked to the autumn menu post while it was still a draft:

```
error   link-broken            /blog/sourdough-starter/ (src/pages/blog/sourdough-starter.md)
        Link to "/blog/autumn-menu/" points to a draft page, which is not published.

Checked 5 page(s): 1 error(s), 0 warning(s).
Passing checks do not guarantee indexing, rankings, rich results or AI citations.
```

Remove `draft: true` when you're ready, rebuild and deploy. vagejs has no built-in scheduler, so "publishing" means removing the line and deploying on the day (or writing a small CI job that does it for you).

## What doesn't the feed do?

So you're not surprised later:

-   **Summaries only.** Each item carries the post's `description`, not the full text. Readers click through to read the post on your site.
-   **RSS 2.0 only.** vagejs writes RSS. It doesn't write Atom or JSON Feed files.
-   **One feed per section.** A feed covers everything under a folder like `/blog/`. You can list several sections in `feeds` (say `/blog/` and `/news/`), but there's no automatic per-tag or per-author feed.
-   **No email newsletter.** A feed is a file people can subscribe to; sending posts by email needs a separate service.
-   **Your site address must be set**, as we saw above, because feed links have to be absolute.

## Blog and feed recap

-   Posts are Markdown files in `src/pages/blog/`; the starter's blog index lists them, newest first.
-   `seo: { feeds: [{ under: "/blog/" }] }` writes `/blog/feed.xml` and links it from every page in the blog.
-   A short layout with `collection()` puts the latest posts on your home page.
-   `draft: true` keeps a post out of everything until you're ready, and `vage check` catches links to it.
-   Feeds hold summaries, not full posts, and need `site` set.

Ready to start? [Getting started](https://vagejs.com/docs/getting-started/) takes a minute, and the [configuration guide](https://vagejs.com/docs/configuration/) has the rest of the `seo` options.

![](https://vagejs.com/images/venkatesh-age-144.webp)

Written by

Venkatesh A G E

Web developer in Bengaluru, India, and the maker of vagejs. Helps startups turn their websites into a real source of customers, with a focus on clarity, technical SEO and AI search.

[LinkedIn](https://www.linkedin.com/in/ageven/)[About](https://vagejs.com/about/)
