{
  "$type": "site.standard.document",
  "content": {
    "$type": "site.standard.content.markdown",
    "text": "I recently remade my website (I know, I know) and I got a surprise when getting to reimplement an rss feed because, while [Astro](https://astro.build) has a module that helps with generating an rss feed, Nuxt doesn't - at least not for V3 and consequently V4. But worry not, for making one is easy enough !\n\n## What is an RSS Feed ?\n\nAn RSS feed is an xml document that allows users to subscribe to updates on a website. It is an xml document that can be easily parsed to retrieve the latest published article, the website's author and more info (wikipedia entry [here](https://en.wikipedia.org/wiki/RSS) if you want to learn more).\n\nThere are a lot of applications that will allow you to subscribe to an rss feed and even get notified when one is updated.\n\nAlthough RSS in its xml format is the most well-known way to consume web feeds, there are other formats that are also widely supported:\n\n- json feeds\n- atom feeds\n\nIn this article I'll only be talking about RSS - and briefly mentioning atom - but all the things I'm talking about apply to all of them.\n\n### The structure of an RSS Feed\n\nLet's take a look at what an rss feed looks like (you can find mine [here](https://www.matteogassend.com/rss.xml)) - specifically the one for my website\n\n```xml\n<rss version=\"2.0\">\n  <channel>\n    <title>Matteo's Log</title>\n    <link>https://www.matteogassend.com</link>\n    <generator>Nuxt & Feed</generator>\n    <language>en</language>\n    <atom:link href=\"https://www.matteogassend.com/rss.xml\" rel=\"self\" type=\"application/rss+xml\"/>\n    <item>\n      <title>T3Chat Cloneathon Postmortem</title>\n      <link>\n      https://www.matteogassend.com/articles/t3-cloneathon-postmortem\n      </link>\n      <guid isPermaLink=\"false\">t3-cloneathon-postmortem</guid>\n      <pubDate>Mon, 14 Jul 2025 00:00:00 GMT</pubDate>\n      <description>\n      I participated in the T3Chat cloneathon; here's how it went\n      </description>\n      <content:encoded>\n        ...\n      </content:encoded>\n    </item>\n  </channel>\n</rss>\n```\n\nFirst we have the main tag specifying that this xml document is an rss feed and its implementation version (in this case, version 2.0).\nWe then get to the **channel** definition; this is the actual feed that will be consumed and inside of it we can find info such as its name, description, language and more.\nThen, for each element we want to add to our **channel**, we have a **item** tag. Each item contains a name, description, publication date, unique id and optionally the raw content of the item (in this case, a blog article).\n\n## How to build one in Nuxt\n\nLuckily, Nuxt routes can be either Vue components or server endpoints - you could also use tsx, but we'll skip that for now. Server - or API - endpoints are handled by Nitro and use the classic browser Request & Response, meaning we can simply return an xml document with the correct headers and we're good to go! Let's see how to do it in detail\n\n### Declaring the API route\n\nLet's start by declaring the Nitro route; in this instance I'll have it match the `/rss.xml` path, meaning I will create a file here: `/server/routes/rss.xml.ts` and fill it with the basic Nitro handlers:\n\n```ts\nexport default defineEventHandler(async (event) => {\n  //generate the feed\n  ...\n  //return the actual feed data here\n  return {}\n})\n```\n\n### The RSS Feed enter the game\n\nWe **could** write the xml document by hand, but why bother? There exist the perfect package for this: [feed](https://github.com/jpmonette/feed). It allows use to programmatically build a feed and return it in different formats. Let's see how we can integrate it with Nitro.\n\nFirst things first, let's install the dependency:\n\n```sh\nnpm install feed\n```\n\nAnd let's start creating the feed itself; we'll begin by setting all the basic info needed for the feed and leave the items for later:\n\n```ts\n  const feed = new Feed({\n    title: \"Matteo's Log\",\n    description: \"All the articles from matteogassend.com\",\n    id: <a unique id>,\n    link: <url of your website>,\n    language: \"en\",\n    copyright: `All rights reserved ${new Date().getFullYear()}, Matteo Gassend`,\n    updated: new Date(),\n    generator: \"Nuxt & Feed\",\n    feedLinks: {\n      rss: `<url of your website>/rss.xml`,\n    },\n    author: {\n      name: <name of the author>,\n    },\n  });\n```\n\nWith just this, we could generate an xml document and return it with the endpoint we made above. Let's do just that\n\n```ts\nexport default defineEventHandler(async (event) => {\n  ... // the feed is here\n\n  return feed.rss2()\n})\n```\n\nWe only need two other things before we have a fully functional rss feed:\n\n- informing the browser of the type of data we're sending\n- inserting the actual items in the feed\n\nThe first step is simple enough, so let's get it out of the way; before returning the generated feed, let's set the `content-type` header. This header tells the browser how to understand the content we'll send it back; you can see it used mostly when sending back json data - when making an api, for example. Here's how we can do it in Nitro:\n\n```ts\nsetResponseHeader(event, 'content-type', 'text/xml')\n```\n\nThis tells the browser we're sending back an xml document.\n\n### Adding Items to the feed\n\nNow that we have our basis covered, let's get to adding actual items to the feed. In my usecase, I needed to fetch items from a Nuxt Content collection and add it to the feed. So let's do just that:\n\n```ts\nconst articles = await queryCollection(event, 'articles')\n  .order('publishDate', 'DESC')\n  .all()\n```\n\nIf you don't have a collection, just pretend this is an array of objects.\n\nAfter that, it's as simple as looping over your data array and calling the `addItem` method on your feed instance:\n\n```ts\narticles.forEach((article) => {\n  feed.addItem({\n    title: article.title,\n    id: article.slug,\n    description: article.summary,\n    date: new Date(article.publishDate),\n    link: `${BASE_URL}/articles/${article.slug}`,\n    content: article.rawbody,\n  })\n})\n```\n\nAnd that's all you need to do to generate an RSS feed in your Nuxt app! I also added an atom feed with the exact same logic - I just had to replace `rss2` call with `atom1` and you're done!\n\n## Adding the Feed to the Website\n\nOnce you have your server route you just need to add it to your pages and most rss readers should be able to pick it up. Let's say our rss feed lives at `/rss.xml`: if so, we'll need to add the following `link` tag to our page's `head`:\n\n```html\n<link rel=\"alternate\" type=\"application/rss+xml\" title=\"Your feed's title\" href=`${BASE_URL}/rss.xml`\n```\n\nIf you're using `unhead`, then this can also be done in a useHead composable hook using something like this:\n\n```ts\nuseHead({\n  link: [\n    {\n      rel: 'alternate',\n      type: 'application/rss+xml',\n      title: \"Matteo's Log\",\n      href: '/rss.xml',\n    },\n    {\n      rel: 'alternate',\n      type: 'application/atom+xml',\n      title: \"Matteo's Log\",\n      href: '/atom',\n    },\n  ],\n})\n```\n\nAnd you're officially done!",
    "version": "1.0"
  },
  "path": "/articles/nuxt-rss-feed",
  "publishedAt": "2025-09-18T00:00:00.000Z",
  "site": "at://did:plc:dgtaz4vldacvqhvvmdvoc4ad/site.standard.publication/3mfbydibiwc7f",
  "tags": [
    "nuxt",
    "javascript",
    "rss"
  ],
  "textContent": "I recently remade my website (I know, I know) and I got a surprise when getting to reimplement an rss feed because, while Astro has a module that helps with generating an rss feed, Nuxt doesn't - at least not for V3 and consequently V4. But worry not, for making one is easy enough !\n\nWhat is an RSS Feed ?\n\nAn RSS feed is an xml document that allows users to subscribe to updates on a website. It is an xml document that can be easily parsed to retrieve the latest published article, the website's author and more info (wikipedia entry here if you want to learn more).\n\nThere are a lot of applications that will allow you to subscribe to an rss feed and even get notified when one is updated.\n\nAlthough RSS in its xml format is the most well-known way to consume web feeds, there are other formats that are also widely supported:\njson feeds\natom feeds\n\nIn this article I'll only be talking about RSS - and briefly mentioning atom - but all the things I'm talking about apply to all of them.\n\nThe structure of an RSS Feed\n\nLet's take a look at what an rss feed looks like (you can find mine here) - specifically the one for my website\n\nFirst we have the main tag specifying that this xml document is an rss feed and its implementation version (in this case, version 2.0).\nWe then get to the channel definition; this is the actual feed that will be consumed and inside of it we can find info such as its name, description, language and more.\nThen, for each element we want to add to our channel, we have a item tag. Each item contains a name, description, publication date, unique id and optionally the raw content of the item (in this case, a blog article).\n\nHow to build one in Nuxt\n\nLuckily, Nuxt routes can be either Vue components or server endpoints - you could also use tsx, but we'll skip that for now. Server - or API - endpoints are handled by Nitro and use the classic browser Request & Response, meaning we can simply return an xml document with the correct headers and we're good to go! Let's see how to do it in detail\n\nDeclaring the API route\n\nLet's start by declaring the Nitro route; in this instance I'll have it match the  path, meaning I will create a file here:  and fill it with the basic Nitro handlers:\n\nThe RSS Feed enter the game\n\nWe could write the xml document by hand, but why bother? There exist the perfect package for this: feed. It allows use to programmatically build a feed and return it in different formats. Let's see how we can integrate it with Nitro.\n\nFirst things first, let's install the dependency:\n\nAnd let's start creating the feed itself; we'll begin by setting all the basic info needed for the feed and leave the items for later:\n\nWith just this, we could generate an xml document and return it with the endpoint we made above. Let's do just that\n\nWe only need two other things before we have a fully functional rss feed:\ninforming the browser of the type of data we're sending\ninserting the actual items in the feed\n\nThe first step is simple enough, so let's get it out of the way; before returning the generated feed, let's set the  header. This header tells the browser how to understand the content we'll send it back; you can see it used mostly when sending back json data - when making an api, for example. Here's how we can do it in Nitro:\n\nThis tells the browser we're sending back an xml document.\n\nAdding Items to the feed\n\nNow that we have our basis covered, let's get to adding actual items to the feed. In my usecase, I needed to fetch items from a Nuxt Content collection and add it to the feed. So let's do just that:\n\nIf you don't have a collection, just pretend this is an array of objects.\n\nAfter that, it's as simple as looping over your data array and calling the  method on your feed instance:\n\nAnd that's all you need to do to generate an RSS feed in your Nuxt app! I also added an atom feed with the exact same logic - I just had to replace  call with  and you're done!\n\nAdding the Feed to the Website\n\nOnce you have your server route you just need to add it to your pages and most rss readers should be able to pick it up. Let's say our rss feed lives at : if so, we'll need to add the following  tag to our page's :\n\nIf you're using , then this can also be done in a useHead composable hook using something like this:\n\nAnd you're officially done!",
  "title": "Making an RSS Feed for a Nuxt Website",
  "updatedAt": "2026-05-17T13:09:08.409Z"
}