Skip to content

skhotdotcom/good-reader

Repository files navigation

Good Reader

A local-first RSS reader inspired by Google Reader. Built with Next.js, SQLite, and Tailwind CSS. Runs entirely on your machine — no accounts, no cloud, no tracking.

Good Reader Screenshot


About this project

I built this because I missed Google Reader. Clean feeds, no algorithm, no ads — just the internet the way I used to learn from it.

I used Claude Code to build it, which means the tool I used to make this is the same thing I was trying to understand. That felt like the right way to learn.

If it brings back the feeling too, star the repo. It helps more people find it.

And if you really love it: buy me a coffee. Hon, you didn't have to, but I appreciate it.


Features

Core Reading Experience

  • Three-panel layout — folders/feeds sidebar, article list, reading pane
  • Two-panel layout — focus mode that swaps between list and reader; toggle in Settings
  • Unread & Starred views — fixed items at the top of the sidebar; Unread is the default view
  • Article content — full article body rendered in a clean typography layout
  • Open original — open the source article in a new tab
  • Mark as read/unread — automatic on open, or toggle manually
  • Star articles — save favorites for later

Feed Management

  • Add feeds — paste any RSS/Atom URL
  • Feed auto-discovery — paste a site URL and Good Reader finds the RSS feed automatically
  • Feed favicons — icons fetched by parsing each site's <link rel="icon"> tag, not just /favicon.ico
  • Folders — organize feeds into collapsible folders (collapsed by default)
  • Refresh All — fetch latest articles from all feeds at once; updates title, URL, and favicon on each refresh
  • OPML import/export — migrate from Google Reader, Feedly, or any other RSS reader

Article Discovery

  • Full-text search — powered by SQLite FTS5; searches titles and article bodies
  • Load more — pagination for feeds with large backlogs (50 articles per page)
  • Auto-scroll — article list keeps the selected item in view during keyboard navigation

Article Tagging

  • Tags — create and manage tags across all articles
  • Tag filtering — click any tag in the sidebar to filter articles by that tag
  • Inline tag manager — add or remove tags directly from the reading pane header
  • Tag pills — tags shown on article cards in the list view (up to 3, with overflow count)
  • Auto-color — tags are automatically assigned colors from a preset palette

AI Features (via LM Studio)

  • Summarize — one-click bullet-point summaries using a local LLM
  • Auto-tag — automatically generates and applies relevant tags to an article using a local LLM; reuses existing tags and creates new ones freely (3–6 tags per article)
  • Both features are configurable: set the server URL and model name in Settings

Keyboard Shortcuts

Key Action
j Next article
k Previous article
o / Enter Open original in browser
s Star / unstar
m Toggle read/unread
r Refresh all feeds
? Show keyboard shortcut help

j, k, o, s, and m are customizable in Settings. r, Enter, and ? are always active.

Settings

  • Switch between 3-panel and 2-panel layout modes
  • Remap any keyboard shortcut from a curated list of alternatives
  • Configure LM Studio server URL and model name
  • Settings persist in localStorage

Tech Stack

Layer Technology
Framework Next.js 16 (App Router)
Styling Tailwind CSS v4 + shadcn/ui
Database SQLite via better-sqlite3
Full-text search SQLite FTS5 virtual table
RSS parsing rss-parser
HTML parsing jsdom (OPML import, feed discovery)
AI LM Studio (OpenAI-compatible local API)
Desktop Electron (wraps Next.js server natively)

Getting Started

Prerequisites

  • Node.js 18+ (download)
  • npm (comes with Node.js)
  • Git

Install & Run

# Clone the repo
git clone https://github.com/scottpurcell/good-reader.git
cd good-reader

# Install dependencies
npm install

# Start the dev server
npm run dev

Open http://localhost:3000 in your browser.

The SQLite database is created automatically at data/good-reader.db on first run — nothing extra to set up.

Verify Node.js version

node -v  # should print v18.x.x or higher

If you need to upgrade Node, use nvm (nvm install 20 && nvm use 20) or download from nodejs.org.

Adding Feeds

  1. Click + Add Feed in the top bar
  2. Paste a feed URL (e.g. https://hnrss.org/frontpage) or a site URL — Good Reader will auto-discover the RSS link
  3. Optionally assign to a folder and click Add Feed

Importing from Another Reader

  1. Export an OPML file from your current reader
  2. Click ···Import OPML in Good Reader
  3. All feeds and folders are imported automatically

Running as a Desktop App (Electron)

Good Reader can run as a native desktop app on macOS and Windows via Electron.

Download a pre-built release:

The easiest way to get the desktop app is to download a release from the GitHub Releases page.

Platform File
macOS Apple Silicon (M1/M2/M3) Good Reader-x.x.x-arm64.dmg
macOS Intel Good Reader-x.x.x.dmg
Windows Good Reader Setup x.x.x.exe

macOS note: Builds are currently unsigned. On first launch, right-click the app → Open to bypass Gatekeeper.

Development (live reload):

npm run electron:dev

This starts Next.js and Electron together. The app opens in a native window.

Build a distributable:

npm run electron:build:mac   # macOS → dist-electron/*.dmg
npm run electron:build:win   # Windows → dist-electron/*.exe
npm run electron:build       # current platform

The build scripts automatically rebuild native modules (better-sqlite3) for the correct Electron Node version before compiling. On macOS, both Intel (x64) and Apple Silicon (arm64) DMGs are built from a single command. Cross-compiling a Windows .exe from macOS uses Wine (downloaded automatically by electron-builder).

The database is stored in the OS user-data directory (~/Library/Application Support/Good Reader/ on macOS, %APPDATA%\Good Reader\ on Windows) so it persists across app updates.

AI Features Setup

  1. Download and open LM Studio
  2. Load a model (e.g. meta-llama-3.1-8b-instruct)
  3. Start the local server (default port: 1234)
  4. In Good Reader: Settings (sidebar) → AI Summarization
  5. Set the model name to match what's shown in LM Studio → Save
  6. Open any article and click ✨ Summarize or 🏷 Auto-tag

Project Structure

app/
  layout.tsx              — root layout, dark mode, Toaster
  page.tsx                — main three-panel UI (all state)
  globals.css             — Tailwind v4 + typography plugin
  api/
    folders/              — GET/POST/PATCH/DELETE folders
    feeds/                — GET/POST/PATCH/DELETE feeds
      discover/           — GET: auto-discover RSS from a site URL
      refresh-all/        — POST: refresh all feeds + update metadata/favicons
      [id]/refresh/       — POST: refresh single feed + update metadata/favicons
    articles/             — GET (with FTS search, tag filter) / PATCH
      [id]/summarize/     — POST: summarize via LM Studio
      [id]/auto-tag/      — POST: generate & apply tags via LM Studio
      [id]/tags/          — POST: add tag to article
      [id]/tags/[tagId]/  — DELETE: remove tag from article
      mark-all-read/      — POST: bulk mark read
    tags/                 — GET: list tags, POST: create tag
      [id]/               — PATCH: rename, DELETE: remove tag
    opml/                 — GET: export, POST: import
components/
  sidebar.tsx             — feed/folder tree + tags section with context menus
  article-list.tsx        — scrollable article cards with tag pills
  reading-pane.tsx        — article content, inline tag manager, AI summary/auto-tag
  top-bar.tsx             — Refresh All, Add Feed, ··· menu
  add-feed-dialog.tsx     — add feed with auto-discovery
  settings-dialog.tsx     — keyboard shortcuts + LM Studio config
  keyboard-shortcuts.tsx  — shortcut reference dialog
lib/
  db.ts                   — SQLite singleton + schema migrations + FTS5 + tags tables
  feed-fetcher.ts         — rss-parser wrapper + async favicon resolution
  keybindings.ts          — keybinding types, defaults, localStorage
  layout.ts               — layout mode types, defaults, localStorage
  lmstudio.ts             — LM Studio config types, localStorage
  utils.ts                — shadcn cn() helper
electron/
  main.js                 — Electron main process; spawns Next.js server + BrowserWindow
  preload.js              — renderer preload (context isolation)
scripts/
  backfill-favicons.mjs   — one-time script to populate favicon_url for existing feeds

Changelog

Completed

  • Three-panel Google Reader–style layout
  • SQLite database with auto-migration on startup
  • Add / remove feeds and folders
  • Refresh individual feeds and refresh all
  • OPML import and export
  • Full-text search (SQLite FTS5)
  • Feed URL auto-discovery from site homepages
  • Load more / infinite scroll pagination
  • Auto-scroll article list on j/k navigation
  • Open all article links in new tab
  • Unread and Starred fixed sidebar items
  • Reading pane scrolls to top on article change
  • Customizable keyboard shortcuts (Settings)
  • Folders collapsed by default
  • "Inactive" folder for stale feeds
  • AI summarization via LM Studio (local LLM)
  • Article tagging with tag-based sidebar filtering
  • Inline tag manager in the reading pane
  • Tag pills on article cards
  • Auto-tag articles with LLM (creates new tags freely)
  • Feed favicons via <link rel="icon"> parsing (not just /favicon.ico)
  • Feed metadata (title, URL, favicon) updated on every refresh
  • Electron wrapper — native desktop app for macOS and Windows
  • GitHub Releases with pre-built DMG and Windows installer
  • Two-panel layout mode (focus view)
  • Settings as a sidebar nav item (mutually exclusive with feed/folder/tag views)
  • Settings: 3-column layout with General, Shortcuts, AI, and Data sections
  • OPML import/export moved into Settings → Data (removed ··· dropdown from top bar)
  • Settings auto-save — all changes apply immediately, no Save/Cancel needed
  • Auto-refresh feeds when switching to Unread view (no separate Refresh All button)
  • 2-panel fix: preserve selected article during background feed refreshes

Backlog

  • Dark / light mode toggle
  • "Read later" queue
  • Mobile-responsive layout
  • PWA support for offline reading
  • Signed macOS DMG (requires Apple Developer account)
  • Per-feed refresh interval settings
  • Article sharing (copy link, share sheet)
  • Swipe gestures on mobile / trackpad

Releasing a New Version

# 1. Bump the version (updates package.json, commits, and creates a git tag)
npm version patch   # bug fixes:    0.2.0 → 0.2.1
npm version minor   # new features: 0.2.0 → 0.3.0
npm version major   # breaking:     0.2.0 → 1.0.0

# 2. Push the commit and tag
git push origin main --tags

# 3. Build the distributables
npm run electron:build:mac   # creates dist-electron/*.dmg
npm run electron:build:win   # creates dist-electron/*.exe

# 4. Create a GitHub Release and upload the artifacts
gh release create vX.X.X \
  "dist-electron/Good Reader-X.X.X-arm64.dmg#macOS (Apple Silicon)" \
  "dist-electron/Good Reader-X.X.X.dmg#macOS (Intel)" \
  "dist-electron/Good Reader Setup X.X.X.exe#Windows Installer" \
  --title "Good Reader vX.X.X" \
  --notes "## What's new
- ..."

Or create the release manually via GitHub → Releases → Draft a new release, pick the tag, and drag the files from dist-electron/ into the assets area.

Note: gh CLI must be authenticated (gh auth login). The build scripts handle npm rebuild better-sqlite3 automatically before each build — no manual step needed.


Development Notes

  • All API routes require export const dynamic = 'force-dynamic' to prevent build-time SQLite errors
  • Tailwind v4: use w-[360px] not w-90 (only multiples of 4 up to w-96 are valid)
  • Flex scroll pattern: flex-1 min-h-0 overflow-y-auto + overflow-hidden on the parent container
  • SQLite FTS5: use actual table name in MATCH query, not an alias
  • Tags use json_group_array / json_object correlated subquery to avoid N+1 queries
  • The database file at data/good-reader.db is gitignored

License

MIT

About

A local-first RSS reader inspired by Google Reader. Built with Next.js, SQLite, and Tailwind CSS. Runs entirely on your machine — no accounts, no cloud, no tracking.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors