cybrkyd

A simple HTML RSS feed reader and aggregator with JavaScript

 Sat, 05 Jul 2025 18:24 UTC
A simple HTML RSS feed reader and aggregator with JavaScript
Image: CC BY 4.0 by cybrkyd

Self-hosted RSS readers and aggregators — I’ve seen some shocking implementations! From the incredibly complex to over-the-top functionality that makes me ask: Why?

I woke up this morning and thought this through; I mean, really burned some brain cells over this one. I have previously used Thunderbird as my RSS reader and it is bad. It is too picky about what it will work with and…it’s a mess! I have also used Evolution but when I went back to Thunderbird, I could not quite justify having Evolution open for the sole purpose of my RSS feeds. Evolution’s RSS functionality works perfectly fine, but since I no longer use it, there’s that.

So, cogs in motion today, I decided to try to solve this. I don’t want a database; that’s a big no-no. How can I still enjoy my RSS feed without (1) having to store anything in a database, and (2) having to keep pruning it? Oh, and no installing anything; I’m tired of these heavy solutions that require a degree in computer science. Funnily enough, I even considered using Python to merge feeds into one and then show that on a web page, but that’s too much!

Here is what I came up with:

Fetching RSS feeds with wget

I’m going to re-name the XML files because most people use index.xml, rss.xml, etc. Let’s call them feed1.xml, feed2.xml and so on.

wget -q -T 10 -t 2 -U "FetchRSS/1.0 (+https://cybrkyd.com/rss-reader)" -O /var/www/html/feeds/feed4.xml https://feeds.bbci.co.uk/news/technology/rss.xml

Stick all the feeds into a bash script and run it at intervals with a trusty cron job.

#!/bin/bash

wget -q -T 10 -t 2 -U "FetchRSS/1.0 (+https://cybrkyd.com/rss-reader)" -O feed1.xml https://82mhz.net/index.xml
wget -q -T 10 -t 2 -U "FetchRSS/1.0 (+https://cybrkyd.com/rss-reader)" -O feed2.xml https://canro91.github.io/atom.xml
wget -q -T 10 -t 2 -U "FetchRSS/1.0 (+https://cybrkyd.com/rss-reader)" -O feed3.xml https://rubenerd.com/feed/
wget -q -T 10 -t 2 -U "FetchRSS/1.0 (+https://cybrkyd.com/rss-reader)" -O feed4.xml https://feeds.bbci.co.uk/news/technology/rss.xml

A simple HTML RSS feed reader and aggregator with JavaScript

Yes, that sub-heading is intentionally written that way to boost SEO of this page because I’m proud of this, and I’m going to shout it from the rooftops, dammit!

I’ve broken-down a problem I had to its smallest atom (no pun intended here, honest) and I’ve managed to keep it simple, free and blazingly fast. It’s light, loads in milliseconds, and it is beautiful to look at!

Here is what it does:

  1. For each RSS feed file (whether RSS or Atom), fetch the feed data from the local files.
  2. Once the data is fetched, extract the title, link, and publication date of each item in the feeds.
  3. Next, collect a limited number of items (up to four) from each feed and store them in a list.
  4. Sort the list by date so that the most recent items appear first.
  5. Finally, add the list to the webpage. Each <li> element displays (i) a clickable link to the item, (ii) the source label, and (iii) the publication date.

See it in action here: Cybrkyd’s FetchRSS

The JS (in file feed.js):

const feeds = [
  { url: 'feed1.xml', label: '82MHz' },
  { url: 'feed2.xml', label: 'Just Some Code' },
  { url: 'feed3.xml', label: 'Rubenerd' },
  { url: 'feed4.xml', label: 'BBC Technology' }
];
const ITEMS_PER_FEED = 4;

async function fetchFeed(url) {
  const res = await fetch(url);
  const text = await res.text();
  const parser = new DOMParser();
  return parser.parseFromString(text, "text/xml");
}

function parseRSS(doc) {
  const items = [...doc.querySelectorAll("item")].slice(0, ITEMS_PER_FEED);
  return items.map(item => ({
    title: item.querySelector("title")?.textContent ?? "(No title)",
    link: item.querySelector("link")?.textContent ?? "#",
    date: new Date(item.querySelector("pubDate")?.textContent ?? 0)
  }));
}

function parseAtom(doc) {
  const entries = [...doc.querySelectorAll("entry")].slice(0, ITEMS_PER_FEED);
  return entries.map(entry => ({
    title: entry.querySelector("title")?.textContent ?? "(No title)",
    link: entry.querySelector("link")?.getAttribute("href") ?? "#",
    date: new Date(entry.querySelector("updated")?.textContent ?? 0)
  }));
}

function parseFeed(doc) {
  if (doc.querySelector("item")) return parseRSS(doc);
  if (doc.querySelector("entry")) return parseAtom(doc);
  return [];
}

(async () => {
  const allItems = [];

  for (const { url, label } of feeds) {
    try {
      const doc = await fetchFeed(url);
      const items = parseFeed(doc).map(item => ({ ...item, source: label }));
      allItems.push(...items);
    } catch (err) {
      console.error(`Error parsing ${url}:`, err);
    }
  }

  allItems.sort((a, b) => b.date - a.date);

  const ul = document.createElement("ul");
  for (const item of allItems) {
    const li = document.createElement("li");

    const a = document.createElement("a");
    a.href = item.link;
    a.textContent = item.title;
    a.target = "_blank";
    a.rel = "noopener noreferrer";

    const sourceSpan = document.createElement("span");
    sourceSpan.className = "source-label";
    sourceSpan.textContent = ` • ${item.source}`;

    const dateSpan = document.createElement("span");
    dateSpan.className = "date-label";
    dateSpan.textContent = ` (${item.date.toISOString().split("T")[0]})`;

    li.appendChild(a);
    li.appendChild(sourceSpan);
    li.appendChild(dateSpan);
    ul.appendChild(li);
  }

  document.getElementById("feed-box").innerHTML = "";
  document.getElementById("feed-box").appendChild(ul);
})();

The HTML snippet:

<script src="feed.js"></script>

Don’t buy me a coffee, please no. Just copy and deploy, and say thanks if you want to. And for goodness’s sake, save your poor server from hard work. RSS = Really Simple Syndication, and this solution takes things back to basics.

»
Tagged in:

Visitors: Loading...