Feedstand
July 31, 2026 · updates · reading

Improvements in June and July

A list of the smaller improvements from June and July. As in May, most of the reading ones went into Feedsweep, with the rest in Feedstand itself. The biggest change of the summer was link previews for most of the web.

Embeds

  • Many blogs ship a lazy-load placeholder instead of a real video, relying on JavaScript a feed reader never runs, so the post showed an empty box. Videos and audio hidden behind these plugins now play.
  • Vimeo, Dailymotion, and JW Player embeds display alongside the already-supported YouTube.
  • Datawrapper charts show as their chart image, linked to the interactive version.

Reading

  • Forum smilies and emoji sprites from around twenty forum and blog engines are swapped for the real character, so text reads and copies correctly.
  • Code blocks show a language badge, diffs style their added and removed lines, and line-number gutters copied from the publisher's highlighter are stripped, so copying code copies only code.
  • Syntax highlighting only applies when the publisher declared a language, so no more wrongly-colored blocks.
  • Cookie-consent plugins leave dead "accept cookies to view this content" boxes in feeds; those are now removed.
  • Podcast and video items get real players, artwork, and durations, including items imported before this change.
  • Email and phone links inside articles survive cleanup instead of being stripped.
  • Reader view keeps embeds, and when extraction fails it falls back to the feed's own content instead of showing nothing.
  • A typography pass on headings, lists, and quotes, so long articles breathe better.

Images

  • Images now load through a proxy, which fixes broken images and keeps reading private.
  • Images keep their real dimensions, so articles stop jumping around while loading.
  • YouTube thumbnails fall back to a real frame when YouTube serves its gray placeholder, and videos without usable dimensions default to 16:9.

Reliability

  • An edited title, a republished article with a bumped date, or a swapped header image no longer shows up as a duplicate item.
  • Feed polling got more reliable and more polite: feeds are checked at a pace matching how they publish, and the servers behind them are respected.
  • Adding a site by a deep URL got smarter: the feed is also looked for at the parent paths and in the page's section links.

One more thing is close: native video and audio uploads from newsletter and podcast platforms becoming playable right in the reader. That is for the next post.

July 25, 2026 · feature · reading

More platforms get link previews

In June I added link preview cards (or unfurls) for bookmark embeds from Ghost and Substack. That was two platforms. This month the list grew a lot longer.

Link preview cards from many different platforms, all rendering as the same card

Forums expand links into boxes, WordPress themes ship "blogcards", newsletter platforms embed article cards. They all carry mostly the same fields, a title, a short description, the source, a thumbnail, but every platform invents its own markup for it. Without special handling, that markup reaches the reader as a broken pile of divs.

Feedstand now recognizes each of these shapes and folds them into one link preview format, so they all render as the same clean card. The cards also keep the caption the author wrote under the link, and show a small label of what kind of link it is.

The support includes:

  • Blogging platforms: dev.to, Medium, Tumblr, Hatena, Ameba, Tistory
  • WordPress themes and plugins: Cocoon, SWELL, AFFINGER, TCD
  • Forums: Discourse, XenForo, NodeBB
  • Embed services: Embedly, Paragraph
  • Open standards: microformats citations

Link previews are one small piece of a bigger plan to bring rich content into the feed reader. There will be more to say about that soon.

July 16, 2026 · feature · privacy

Images now load through a proxy

Until now, every image in an article loaded straight from the publisher's server. That meant every image you opened told that server about you: your IP address, your browser, and the exact moment you read the post. You never visited the site, but it learned your reading habits anyway. Some images also didn't load at all.

Article images are now served through Feedstand's own image proxy. Feedstand fetches the image and hands it to you, so the publisher sees a fetch from Feedstand rather than a request from each reader.

Nothing else about you is sent along with it. Feedstand asks for the image and nothing more, so your cookies and browser details stay where they are.

Images that used to be broken now load too. And an image that many readers open is fetched from the publisher once, then served from cache after that.

June 5, 2026 · feature · reading

Link previews for Ghost and Substack

Ghost and Substack let writers link out to another article with a bookmark embed. These now display properly in the reader as a clean link preview, with the linked page's title, a short description, its source, and a thumbnail.

Link preview cards in the reader

May 31, 2026 · updates · reading

Minor improvements in May

A list of the smaller improvements from May. Most of the reading ones went into Feedsweep, the package that tidies up feed content before it reaches the reader, with the rest in Feedstand itself.

Reading

  • Emoji that publishers ship as tiny images are swapped for the actual character.
  • When an item repeats its own title as the first heading in the body, that duplicate heading is removed so the title only shows once.
  • When an item's body has its own <h1>, it collides with the reader's page title, so every heading is shifted down one level (<h1> becomes <h2>, and so on) to fix the hierarchy.
  • Redundant bold wrapping a whole heading is dropped.
  • Wide tables now sit in their own scrollable strip with a soft fade on whichever edge still has more to scroll, instead of spilling past the reading pane and getting clipped.
  • Item titles render as plain, fully decoded text with no stray HTML left over.
  • Clutter is stripped out: share buttons, unrelated post widgets.

Reliability

  • The fetcher now identifies itself with its subscriber count and feed id, so publishers can see who is fetching and how many readers depend on the feed.
  • A feed that keeps returning 404 is given more attempts before being marked discontinued, so a brief outage no longer drops it.
April 22, 2026 · feature · auth

Auth improvements and passkey support

The authentication screens got a long-pending visual refresh. During development they were always second priority and never truly finalized, so I gave them a proper pass this time around. All the auth flows now share one consistent look.

Email verification after signup is wired up, and password reset links go out by email. Plain text for now.

Sign-in screen

Another recent improvement is support for passkey login. You can now register passkeys in the settings and use them the next time you sign in. To learn more about passkeys, visit the official website.

April 20, 2026 · feature · discovery

Feed discovery optimizations

Add Source screen

When you paste a site URL into Feedstand to add a feed, there used to be a short wait while it visited the site, scanned the page, and figured out which feed was the right one. That wait is gone for sites that were already seen. The feed shows up right away.

Given that Feedstand has a large collection of feeds already, there's a good chance the site you're pasting is one of them. When it is, the feeds come back straight from what's stored, no visit to the site needed.

March 31, 2026 · throwback · frontend

Extracting Feedsweep and frontend optimizations

This month I extracted yet another chunk of the feed processing into its own package.

Most feeds need work to display properly in a reader — fixing lazy-loaded images, resolving relative URLs, normalizing broken markup, embedding media, and similar de-quirking. The code for all of that (and more) has been growing inside Feedstand for a while. Recently, this special treatment of feeds got quite big, so I cut it out and shipped it as Feedsweep.


The frontend got a polish pass: virtualized sidebar with section headers and keyboard navigation, optimistic interactions, a full settings dialog, and React Compiler taking over manual memoization.

February 28, 2026 · throwback · architecture

Hooks architecture and Feedmatch

This month I built an internal hooks system to separate the core reader logic from platform-specific behavior. The feed processing pipeline now exposes 20+ named lifecycle points where extensions can hook in. YouTube and GitHub integrations, logging, metrics — all of them moved out into standalone extensions on top of the same machinery.

Down the line, the same surface could be opened up so anyone can write their own extensions for Feedstand.


As somewhat of a tradition now, I've decided to extract the item deduplication logic into a separate package to make it more manageable. I've released it as Feedmatch.


The frontend got another push: OPML import with a progress dialog, infinite-scroll pagination, channel favicons, and a per-source layout system with cascading overrides.

On the API side: rate limiting, auth on feed routes, a per-user source cap, and ID encryption so internal numeric IDs don't leak in URLs.

January 31, 2026 · throwback · frontend

Frontend takes shape

Now that the backend part is stable and robust, it's time for me to build the actual reader interface.

Since Feedstand is meant to be a traditional feed reader in the UI sense, I've built the sidebar, reader view with split-pane, source management, sign in and sign up, mark-as-read, starring, and tags.

I also rewrote item deduplication from scratch. The old approach matched items on a single identifier and kept quietly falling apart in real feeds — publishers change guids between fetches, the same item shows up under several identifiers, and what counts as "the same item" varies between sources. The new logic OR-matches across guid, link, title-with-date, and a normalized identifier hash.

December 31, 2025 · throwback · infra

Extracting Feedcanon

Another month, another new package. I've extracted the canonical URL resolution logic into a standalone library which I called Feedcanon.

The same feed is often accessible through different URLs — mixed schemes, trailing slashes, query params. The logic for collapsing those duplicates has been growing inside Feedstand for a while.

The more I worked on it, the more it looked like a problem worth solving in isolation, the same approach as Feedsmith and Feedscout earlier.

Feedcanon now resolves a candidate URL down to its canonical form before subscription, so duplicates collapse into a single feed instead of slipping through as separate ones.


On the internal tooling side, the project migrated fully from npm to Bun, took a shot at upgrading Drizzle to 1.0 beta, and removed a pile of unused packages.

November 30, 2025 · throwback · performance

Performance work and Feedscout

I got back to Feedstand after the Feedsmith push over the past few weeks.

Went deep into improving the performance across the entire feed processing pipeline (what actually gets and stores the content from feeds). It is now significantly faster and uses less memory, enabling the processing of more feeds in the same amount of time.

I also decided to extract the feed discovery logic into a new package, Feedscout. As a separate project, it gets more room to grow and to support various methods of feed discovery. All while keeping the feed reader codebase lean.

October 31, 2025 · throwback · libraries

Work on Feedsmith continues

Over the last few months, Feedstand sat untouched as I dedicated all my time to Feedsmith. What started as a feed parser can now generate feeds, support OPML, and handle many feed namespaces.

Feedsmith is now effectively production-ready. It feels great to build a library that is faster and robust than many alternatives.

Time to bring it back into Feedstand and finally replace the internal feed handling.

May 31, 2025 · throwback · libraries

Building the feed parser

I've decided to move away from rss-parser. It can't handle the variety of real-world feeds and it's quite slow. Rather than keep patching around its limitations, I started building my own parser.

This is how Feedsmith came to be. It's designed from the ground up to handle the mess that real-world feeds are. It's also very extensible to allow me to add more custom namespaces in the future. It got a nice reception on Hacker News, too.

Now, a general note. The more I work on Feedstand, the more I realize that a feed reader is more like a stack of separate problems pretending to be one project: fetching, parsing, discovery, content cleanup.

February 28, 2025 · throwback · infra

Scaling and feed engine

Came back to the project after a three-month break. I've spent a week or so on the deployment plumbing: CI for Docker images, routing, database migrations, plus a reworked fetch layer with conditional requests so unchanged feeds get skipped. Date parsing keeps growing in scope… feeds use every imaginable format.

As usual at the start of each project, you need to think about scaling it! I've configured multiple worker replicas, database connection pooling, memory limits, and job queuing. I also implemented basic item deduplication via content checksums, and added a feed fix suggestions system that flags broken channels and proposes corrections.

The system now reliably scans thousands of feeds across 2 replicas of the same Docker container of the backend.

October 31, 2024 · throwback

Project bootstrap

After contemplating the idea of creating my own feed reader for a while, I finally took the plunge. I've set up the React frontend and the monorepo plumbing, and gone through three backend frameworks before settling on Hono.

I spent a big part of the time on the feed engine: parsing XML-based and JSON feeds, discovering them from arbitrary URLs (including YouTube). Real-world feeds turn out to be much messier than I expected. They feature non-standard date formats, numerous namespaces, and feeds that technically violate the specifications yet still need to function. Each one needs its own little patch, and the pile of custom treatments is already starting to feel like its own thing.

The core pipeline now runs end to end: discover a feed, subscribe to it, scan for new items on a schedule, and serve them as API responses.