{
  "attachments": [
    "./feedspool-screenshot.png",
    "./feedspool-feed-navigator-screenshot.png"
  ],
  "title": "feedspool-go v0.2.0: Smooth Scrolling and Quiet Feeds",
  "date": "2026-01-26T19:00:00.000Z",
  "tags": [
    "feedspool",
    "golang",
    "rss",
    "webdev",
    "javascript",
    "webcomponents",
    "indieweb"
  ],
  "layout": "post",
  "year": "2026",
  "month": "01",
  "day": "26",
  "isDir": true,
  "slug": "feedspool-go-v0-2-0",
  "type": "entry",
  "postName": "2026-01-26-feedspool-go-v0-2-0",
  "html": "<p><em>TL;DR</em>: Released v0.2.0 of feedspool-go, my static-site RSS reader. Added features to keep infrequent feeds from vanishing and an over-engineered web component lazy-loading system that progressively loads content as you scroll.</p>\n<!--more-->\n\n\n\n<nav role=\"navigation\" class=\"table-of-contents\"></nav>\n\n<h2 id=\"what-is-feedspool-go\">What is feedspool-go?</h2>\n<p>I started this thing last fall, but I don't think I've blogged about it much yet.</p>\n<p><a href=\"https://github.com/lmorchard/feedspool-go\">feedspool-go</a> is a CLI tool I built for fetching &amp; reading RSS feeds. It takes OPML &amp; text lists of feeds, fetches those feeds periodically into a SQLite database, and produces static HTML as a reading interface. </p>\n<p>The \"spool\" part is <a href=\"https://en.wikipedia.org/wiki/Spooling#Other_applications\">inspired by Usenet</a> and the \"-go\" part is because I've <a href=\"https://github.com/lmorchard?tab=repositories&amp;q=feedspool&amp;type=&amp;language=&amp;sort=\">started and abandoned</a> several iterations of this thing in other languages over the years. Maybe this one will stick and I'll more creatively rename it later. 😅</p>\n<p>This runs every 15 minutes on one of my machines at home. If the web server's running, you might be able to read my news at <a href=\"https://feedspool.lmorchard.com/\">feedspool.lmorchard.com</a>.</p>\n<p>Here's a screenshot from mid-scroll:</p>\n<figure>\n  <img src=\"./feedspool-screenshot.png\" alt=\"feedspool-go main reading view screenshot\" width=\"\" height=\"\">\n  <figcaption>feedspool-go main reading view screenshot</figcaption>\n</figure>\n\n<h3 id=\"river-of-news-vs-inbox-model\">River of News vs Inbox Model</h3>\n<p>The key thing is this: I don't want an inbox of unread items. I want to scroll through a personal newspaper of recent content from the web - I stop reading when I see stuff I saw before. This basically does that.</p>\n<p>This follows what Dave Winer calls the <a href=\"http://scripting.com/2014/06/02/whatIsARiverOfNewsAggregator.html\">\"River of News\"</a> pattern, which is fundamentally different from inbox-style RSS readers:</p>\n<ul>\n<li><strong>No read/unread flags</strong> - no state to manage, no guilt about unread counts</li>\n<li><strong>No deleting or archiving</strong> - content flows by from new to old</li>\n<li><strong>No inbox zero pressure</strong> - scroll until you see familiar stuff, then stop</li>\n<li><strong>Ephemeral by design</strong> - old content ages out, the database is just a cache</li>\n</ul>\n<p>Many RSS readers treat feeds like email - every item is a task to process. That makes me anxious and feel like I have make number go down. The river model is liberating: you dip in, catch up on what's new, move on.</p>\n<p>Where I riff on Dave's usual river design is that I like to more strictly group items from the same feed together. And, I really like the card layout with preview images. But, it's basically inspired by his idea.</p>\n<h2 id=\"whats-new-in-v020\">What's new in v0.2.0?</h2>\n<p>I'd mostly left this thing alone since an initial burst of hacking last fall. I've been using it daily, but accumulated a few wishlist items over time. Decided this past weekend I had time to tackle a some of them. Then, I figured that was worth a new <a href=\"https://github.com/lmorchard/feedspool-go/compare/v0.1.0...v0.2.0\">v0.2.0 release</a>. </p>\n<p>This release has a two major themes: ensuring infrequently updated feeds don't disappear, and adding navigation features to make browsing more pleasant.</p>\n<h2 id=\"the-problem-with-quiet-feeds\">The Problem with Quiet Feeds</h2>\n<p>The main driver for this release was <a href=\"https://github.com/lmorchard/feedspool-go/issues/20\">issue </a><a href=\"/tag/20\">#20</a>: when you filter feeds by timespan (e.g., \"show me feeds updated from the last 24 hours\"), feeds that update infrequently just vanish. My favorite once-a-week blog gets completely hidden when more active feeds dominate the view.</p>\n<h3 id=\"minimum-items-per-feed\">Minimum Items Per Feed</h3>\n<p>My solution was adding <code>--min-items-per-feed</code> to the render command. Now the query logic ensures I see <code>MAX(items in timespan, minimum items)</code>. So if a feed has zero items in the last 24 hours, I'll still see its 5 most recent items. The purge command got similar treatment with <code>--min-items</code> to protect history for quiet feeds during cleanup operations.</p>\n<p>I sort the overall display by last-updated time of each feed, so the quiet feeds show up toward the bottom of the scroll. But, I can still see them. I'm considering adding another page devoted to just these feeds, though.</p>\n<h2 id=\"progressive-loading-with-web-components\">Progressive Loading with Web Components</h2>\n<p>The part where I really fell down a webdev rabbit hole in this release was with the lazy loading architecture. Currently, feedspool-go is primarily a static site generator - it renders everything to HTML files during the <code>render</code> command. But with hundreds of feeds and thousands of items, loading everything at once would be painful.</p>\n<p>The key is scrolling, though: that's my main signal to the web UI that I want to read more. So, I wanted to build a UI that responded to that signal.</p>\n<h3 id=\"static-content-dynamically-loaded\">Static Content, Dynamically Loaded</h3>\n<p>The Go program generates:</p>\n<ul>\n<li>An <code>index.html</code> with skeleton structure</li>\n<li>Individual <code>feeds/{id}.html</code> files for each feed</li>\n<li><code>feeds/page-{n}.html</code> files when pagination is enabled</li>\n</ul>\n<p>But here's the clever bit: the index.html doesn't embed much content at all. Instead, it wraps each feed (or page of feeds) in a custom <code>&lt;link-loader&gt;</code> web component as a stub:</p>\n<pre><code class=\"language-html\">&lt;feed-navigator&gt;\n  &lt;link-loader&gt;\n    &lt;h2&gt;My Favorite Blog&lt;/h2&gt;\n    &lt;a href=\"feeds/123.html#feed-123\"&gt;Read more...&lt;/a&gt;\n  &lt;/link-loader&gt;\n  &lt;!-- More link-loaders... --&gt;\n&lt;/feed-navigator&gt;\n</code></pre>\n<h3 id=\"the-link-loader-component\">The link-loader Component</h3>\n<p>The <a href=\"https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/link-loader.js\"><code>link-loader</code> custom element</a> is pretty simple, but does a lot of heavy lifting. When it appears in the viewport (detected via <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API\">IntersectionObserver</a>), it:</p>\n<ol>\n<li>Fetches the URL from its child anchor tag</li>\n<li>Parses the HTML response</li>\n<li>Extracts the fragment (the element matching the <code>#feed-123</code> anchor)</li>\n<li>Inserts that content into the DOM in place of itself</li>\n<li>Removes itself from the DOM</li>\n</ol>\n<p>It's essentially progressive enhancement for static HTML - you get the anchor link as a fallback, but JavaScript hijacks it to load inline on demand just before it scrolls into view.</p>\n<h4 id=\"shared-singleton-observers\">Shared Singleton Observers</h4>\n<p>One optimization worth highlighting: instead of each <code>link-loader</code> creating its own IntersectionObserver, they all share singleton instance created at the module level where the web component is defined. </p>\n<pre><code class=\"language-javascript\">let sharedObserver = null;\n\nfunction getSharedObserver() {\n    if (!sharedObserver) {\n        sharedObserver = createLazyLoadObserver(...);\n    }\n    return sharedObserver;\n}`\n</code></pre>\n<p>This is more efficient than creating hundreds of observers for each link-loader. The browser can optimize intersection calculations across all elements at once, and it reduces memory overhead. Each component just registers itself with the shared observer when it mounts, and unregisters when it unmounts.</p>\n<h3 id=\"the-loadqueue-generic-resource-loading\">The LoadQueue: Generic Resource Loading</h3>\n<p>Another challenge from here: you can't fire off hundreds of fetch requests simultaneously. Well, you can, but the browser gets a bit cranky. So, I built <a href=\"https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/utils/load-queue.js\">a generic <code>LoadQueue</code> class</a> that manages queued loads with tuneable concurrency:</p>\n<pre><code class=\"language-javascript\">class LoadQueue {\n  constructor({\n    maxConcurrent = 1,  // How many loads at once?\n    isVisible,          // (function) Is this item currently visible?\n    isLoaded,           // (function) Already loaded?\n    startLoad,          // (function) How to load it\n  }) { /* ... */ }\n}\n</code></pre>\n<h4 id=\"double-check-visibility-optimization\">Double-Check Visibility Optimization</h4>\n<p>A subtle but important optimization: when a <code>link-loader</code> triggers an IntersectionObserver event, it gets enqueued immediately. But the queue doesn't start fetching right away - it waits its turn based on concurrency limits. Then, the LoadQueue re-checks visibility when it's actually time to fetch:</p>\n<pre><code class=\"language-javascript\">process() {\n  while (this.currentlyLoading.size &lt; this.maxConcurrent &amp;&amp; this.queue.length &gt; 0) {\n    const item = this.queue.shift();\n\n    // Check visibility RIGHT NOW, not when enqueued\n    if (!this.isVisible(item)) {\n      continue;  // Skip and try next item\n    }\n\n    // Actually start loading\n    this.startLoad(item, onComplete);\n  }\n}\n</code></pre>\n<p>Why does this matter? Because earlier loads in the queue inject content into the DOM, which pushes subsequent elements down the page. An element that was near-visible when it entered the queue might be way off-screen by the time its turn comes up. This re-check prevents wasted fetches for content that was scrolled away while waiting in the queue.</p>\n<p>Before I implemented this, I'd get dozens of feeds loading into the page unseen, just because their narrow stubs were briefly visible before they got pushed down. I actually managed to crash the browser on my phone with this, a few times. Now, only the stuff I actually scroll to gets fetched.</p>\n<h3 id=\"the-feed-navigator-component\">The feed-navigator Component</h3>\n<p>Since I group items from the same feed together, I wanted a way to easily skip to the next feed once I started seeing familiar content. So, I built <a href=\"https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/feed-navigator.js\">a <code>feed-navigator</code> component</a> that wraps all the feed loaders and provides prev/next buttons and a dropdown to jump to specific feeds.</p>\n<figure>\n  <img src=\"./feedspool-feed-navigator-screenshot.png\" alt=\"feed-navigator UI screenshot\" width=\"\" height=\"\">\n  <figcaption>feed-navigator UI screenshot</figcaption>\n</figure>\n\n<p>The <code>&lt;feed-navigator&gt;</code> also uses a CSS trick worth mentioning: <a href=\"https://css-tricks.com/get-ready-for-display-contents/\"><code>display: contents</code></a>, which I only really learned about when I started wrestling more with custom elements.</p>\n<p>This is perfect for wrapper components that add behavior but shouldn't affect layout. The <code>feed-navigator</code> wraps all the feed loaders in the DOM tree, giving it control over them, but it doesn't create a containing block or participate in layout. It's completely transparent to CSS.</p>\n<p>The component itself:</p>\n<ul>\n<li>Uses <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver\">IntersectionObserver</a> to track which feed is currently at the top</li>\n<li>Uses <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver\">MutationObserver</a> to detect when new feeds are loaded</li>\n<li>Provides prev/next buttons and a dropdown selector for navigation</li>\n<li>Updates button state based on current position</li>\n</ul>\n<h3 id=\"two-level-loading-pages-and-feeds\">Two-Level Loading: Pages and Feeds</h3>\n<p>When pagination is enabled, there's a two-level hierarchy:</p>\n<ol>\n<li><strong>Page loaders</strong>: Load chunks of ~10 feeds at a time from <code>page-{n}.html</code> files</li>\n<li><strong>Feed loaders</strong>: Load individual feed content from <code>feeds/{id}.html</code> files</li>\n</ol>\n<p>When a page loader fires, it injects more <code>link-loader</code> elements (for individual feeds) into the DOM. The feed navigator's <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver\">MutationObserver</a> detects these new elements and starts observing them too.</p>\n<p>I found this two-level system works really well for my use case, to help spread out the feed loading. The initial page load is super fast since only a few page stubs are loaded. As I scroll, more pages load, and within those pages, individual feeds load as I approach them.</p>\n<h2 id=\"other-miscellanea\">Other Miscellanea</h2>\n<ul>\n<li>Fixed handling of duplicate items (<a href=\"https://github.com/lmorchard/feedspool-go/pull/26\">looking at you, BBC feeds</a>)</li>\n<li>Added <code>--max-items</code> option for capping rendered items</li>\n<li>Fixed GitHub Actions workflows and Docker builds for proper static linking</li>\n</ul>\n<h2 id=\"summing-up\">Summing up</h2>\n<p>This release makes feedspool-go more comfy for my daily RSS reading. The minimum items feature helps me see quieter feeds and the lazy loading system makes it snappy even with my large feed collection.</p>\n<p>Next up: I'm thinking about keyboard shortcuts and maybe further specialized feed pages. Also considering tinkering with some more advanced stuff with the database, like running items through an embedding model so I can do some topical clustering or search.</p>\n<p>I might also just wander off and do nothing else with this until summer. Who knows?</p>\n<p>Anyway, the full changelog for v0.2.0 is <a href=\"https://github.com/lmorchard/feedspool-go/compare/v0.1.0...v0.2.0\">on GitHub</a>. Check it out if it sounds handy!</p>\n",
  "body": "*TL;DR*: Released v0.2.0 of feedspool-go, my static-site RSS reader. Added features to keep infrequent feeds from vanishing and an over-engineered web component lazy-loading system that progressively loads content as you scroll.\n\n<!--more-->\n\n<nav role=\"navigation\" class=\"table-of-contents\"></nav>\n\n## What is feedspool-go?\n\nI started this thing last fall, but I don't think I've blogged about it much yet.\n\n[feedspool-go](https://github.com/lmorchard/feedspool-go) is a CLI tool I built for fetching & reading RSS feeds. It takes OPML & text lists of feeds, fetches those feeds periodically into a SQLite database, and produces static HTML as a reading interface. \n\nThe \"spool\" part is [inspired by Usenet](https://en.wikipedia.org/wiki/Spooling#Other_applications) and the \"-go\" part is because I've [started and abandoned](https://github.com/lmorchard?tab=repositories&q=feedspool&type=&language=&sort=) several iterations of this thing in other languages over the years. Maybe this one will stick and I'll more creatively rename it later. 😅\n\nThis runs every 15 minutes on one of my machines at home. If the web server's running, you might be able to read my news at [feedspool.lmorchard.com](https://feedspool.lmorchard.com/).\n\nHere's a screenshot from mid-scroll:\n\n<figure>\n  <img src=\"./feedspool-screenshot.png\" alt=\"feedspool-go main reading view screenshot\">\n  <figcaption>feedspool-go main reading view screenshot</figcaption>\n</figure>\n\n### River of News vs Inbox Model\n\nThe key thing is this: I don't want an inbox of unread items. I want to scroll through a personal newspaper of recent content from the web - I stop reading when I see stuff I saw before. This basically does that.\n\nThis follows what Dave Winer calls the [\"River of News\"](http://scripting.com/2014/06/02/whatIsARiverOfNewsAggregator.html) pattern, which is fundamentally different from inbox-style RSS readers:\n\n- **No read/unread flags** - no state to manage, no guilt about unread counts\n- **No deleting or archiving** - content flows by from new to old\n- **No inbox zero pressure** - scroll until you see familiar stuff, then stop\n- **Ephemeral by design** - old content ages out, the database is just a cache\n\nMany RSS readers treat feeds like email - every item is a task to process. That makes me anxious and feel like I have make number go down. The river model is liberating: you dip in, catch up on what's new, move on.\n\nWhere I riff on Dave's usual river design is that I like to more strictly group items from the same feed together. And, I really like the card layout with preview images. But, it's basically inspired by his idea.\n\n## What's new in v0.2.0?\n\nI'd mostly left this thing alone since an initial burst of hacking last fall. I've been using it daily, but accumulated a few wishlist items over time. Decided this past weekend I had time to tackle a some of them. Then, I figured that was worth a new [v0.2.0 release](https://github.com/lmorchard/feedspool-go/compare/v0.1.0...v0.2.0). \n\nThis release has a two major themes: ensuring infrequently updated feeds don't disappear, and adding navigation features to make browsing more pleasant.\n\n## The Problem with Quiet Feeds\n\nThe main driver for this release was [issue #20](https://github.com/lmorchard/feedspool-go/issues/20): when you filter feeds by timespan (e.g., \"show me feeds updated from the last 24 hours\"), feeds that update infrequently just vanish. My favorite once-a-week blog gets completely hidden when more active feeds dominate the view.\n\n### Minimum Items Per Feed\n\nMy solution was adding `--min-items-per-feed` to the render command. Now the query logic ensures I see `MAX(items in timespan, minimum items)`. So if a feed has zero items in the last 24 hours, I'll still see its 5 most recent items. The purge command got similar treatment with `--min-items` to protect history for quiet feeds during cleanup operations.\n\nI sort the overall display by last-updated time of each feed, so the quiet feeds show up toward the bottom of the scroll. But, I can still see them. I'm considering adding another page devoted to just these feeds, though.\n\n## Progressive Loading with Web Components\n\nThe part where I really fell down a webdev rabbit hole in this release was with the lazy loading architecture. Currently, feedspool-go is primarily a static site generator - it renders everything to HTML files during the `render` command. But with hundreds of feeds and thousands of items, loading everything at once would be painful.\n\nThe key is scrolling, though: that's my main signal to the web UI that I want to read more. So, I wanted to build a UI that responded to that signal.\n\n### Static Content, Dynamically Loaded\n\nThe Go program generates:\n- An `index.html` with skeleton structure\n- Individual `feeds/{id}.html` files for each feed\n- `feeds/page-{n}.html` files when pagination is enabled\n\nBut here's the clever bit: the index.html doesn't embed much content at all. Instead, it wraps each feed (or page of feeds) in a custom `<link-loader>` web component as a stub:\n\n```html\n<feed-navigator>\n  <link-loader>\n    <h2>My Favorite Blog</h2>\n    <a href=\"feeds/123.html#feed-123\">Read more...</a>\n  </link-loader>\n  <!-- More link-loaders... -->\n</feed-navigator>\n```\n\n### The link-loader Component\n\nThe [`link-loader` custom element](https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/link-loader.js) is pretty simple, but does a lot of heavy lifting. When it appears in the viewport (detected via [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)), it:\n\n1. Fetches the URL from its child anchor tag\n2. Parses the HTML response\n3. Extracts the fragment (the element matching the `#feed-123` anchor)\n4. Inserts that content into the DOM in place of itself\n5. Removes itself from the DOM\n\nIt's essentially progressive enhancement for static HTML - you get the anchor link as a fallback, but JavaScript hijacks it to load inline on demand just before it scrolls into view.\n\n#### Shared Singleton Observers\n\nOne optimization worth highlighting: instead of each `link-loader` creating its own IntersectionObserver, they all share singleton instance created at the module level where the web component is defined. \n\n```javascript\nlet sharedObserver = null;\n\nfunction getSharedObserver() {\n    if (!sharedObserver) {\n        sharedObserver = createLazyLoadObserver(...);\n    }\n    return sharedObserver;\n}`\n```\n\nThis is more efficient than creating hundreds of observers for each link-loader. The browser can optimize intersection calculations across all elements at once, and it reduces memory overhead. Each component just registers itself with the shared observer when it mounts, and unregisters when it unmounts.\n\n### The LoadQueue: Generic Resource Loading\n\nAnother challenge from here: you can't fire off hundreds of fetch requests simultaneously. Well, you can, but the browser gets a bit cranky. So, I built [a generic `LoadQueue` class](https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/utils/load-queue.js) that manages queued loads with tuneable concurrency:\n\n```javascript\nclass LoadQueue {\n  constructor({\n    maxConcurrent = 1,  // How many loads at once?\n    isVisible,          // (function) Is this item currently visible?\n    isLoaded,           // (function) Already loaded?\n    startLoad,          // (function) How to load it\n  }) { /* ... */ }\n}\n```\n\n#### Double-Check Visibility Optimization\n\nA subtle but important optimization: when a `link-loader` triggers an IntersectionObserver event, it gets enqueued immediately. But the queue doesn't start fetching right away - it waits its turn based on concurrency limits. Then, the LoadQueue re-checks visibility when it's actually time to fetch:\n\n```javascript\nprocess() {\n  while (this.currentlyLoading.size < this.maxConcurrent && this.queue.length > 0) {\n    const item = this.queue.shift();\n\n    // Check visibility RIGHT NOW, not when enqueued\n    if (!this.isVisible(item)) {\n      continue;  // Skip and try next item\n    }\n\n    // Actually start loading\n    this.startLoad(item, onComplete);\n  }\n}\n```\n\nWhy does this matter? Because earlier loads in the queue inject content into the DOM, which pushes subsequent elements down the page. An element that was near-visible when it entered the queue might be way off-screen by the time its turn comes up. This re-check prevents wasted fetches for content that was scrolled away while waiting in the queue.\n\nBefore I implemented this, I'd get dozens of feeds loading into the page unseen, just because their narrow stubs were briefly visible before they got pushed down. I actually managed to crash the browser on my phone with this, a few times. Now, only the stuff I actually scroll to gets fetched.\n\n### The feed-navigator Component\n\nSince I group items from the same feed together, I wanted a way to easily skip to the next feed once I started seeing familiar content. So, I built [a `feed-navigator` component](https://github.com/lmorchard/feedspool-go/blob/main/internal/renderer/assets/js/feed-navigator.js) that wraps all the feed loaders and provides prev/next buttons and a dropdown to jump to specific feeds.\n\n<figure>\n  <img src=\"./feedspool-feed-navigator-screenshot.png\" alt=\"feed-navigator UI screenshot\">\n  <figcaption>feed-navigator UI screenshot</figcaption>\n</figure>\n\nThe `<feed-navigator>` also uses a CSS trick worth mentioning: [`display: contents`](https://css-tricks.com/get-ready-for-display-contents/), which I only really learned about when I started wrestling more with custom elements.\n\nThis is perfect for wrapper components that add behavior but shouldn't affect layout. The `feed-navigator` wraps all the feed loaders in the DOM tree, giving it control over them, but it doesn't create a containing block or participate in layout. It's completely transparent to CSS.\n\nThe component itself:\n- Uses [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver) to track which feed is currently at the top\n- Uses [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) to detect when new feeds are loaded\n- Provides prev/next buttons and a dropdown selector for navigation\n- Updates button state based on current position\n\n### Two-Level Loading: Pages and Feeds\n\nWhen pagination is enabled, there's a two-level hierarchy:\n\n1. **Page loaders**: Load chunks of ~10 feeds at a time from `page-{n}.html` files\n2. **Feed loaders**: Load individual feed content from `feeds/{id}.html` files\n\nWhen a page loader fires, it injects more `link-loader` elements (for individual feeds) into the DOM. The feed navigator's [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) detects these new elements and starts observing them too.\n\nI found this two-level system works really well for my use case, to help spread out the feed loading. The initial page load is super fast since only a few page stubs are loaded. As I scroll, more pages load, and within those pages, individual feeds load as I approach them.\n\n## Other Miscellanea\n\n- Fixed handling of duplicate items ([looking at you, BBC feeds](https://github.com/lmorchard/feedspool-go/pull/26))\n- Added `--max-items` option for capping rendered items\n- Fixed GitHub Actions workflows and Docker builds for proper static linking\n\n## Summing up\n\nThis release makes feedspool-go more comfy for my daily RSS reading. The minimum items feature helps me see quieter feeds and the lazy loading system makes it snappy even with my large feed collection.\n\nNext up: I'm thinking about keyboard shortcuts and maybe further specialized feed pages. Also considering tinkering with some more advanced stuff with the database, like running items through an embedding model so I can do some topical clustering or search.\n\nI might also just wander off and do nothing else with this until summer. Who knows?\n\nAnyway, the full changelog for v0.2.0 is [on GitHub](https://github.com/lmorchard/feedspool-go/compare/v0.1.0...v0.2.0). Check it out if it sounds handy!\n",
  "parentPath": "./content/posts/2026/2026-01-26-feedspool-go-v0-2-0",
  "path": "2026/01/26/feedspool-go-v0-2-0",
  "thumbnail": "/2026/01/26/feedspool-go-v0-2-0/feedspool-screenshot.png",
  "summary": "TL;DR: Released v0.2.0 of feedspool-go, my static-site RSS reader. Added features to keep infrequent feeds from vanishing and an over-engineered web component lazy-loading system that progressively loads content as you scroll.",
  "needsBuild": true,
  "prevPostPath": "2026/01/23/w04",
  "prevPostTitle": "2026 Weeks 3 & 4",
  "nextPostPath": "2026/01/28/mermaid-web-component",
  "nextPostTitle": "Wrapping Mermaid Diagrams in a Web Component"
}