Skip to content
Live demo

Performance Optimization

Because SparkFeed reads from a local SQLite database, most operations are already near-instant. A query that would take 100–500ms on a remote database takes under 1ms locally.

That said, with many feeds and years of articles, there are a few things worth tuning.

SQLite’s VACUUM command rebuilds the database file to reclaim space and improve query performance:

npm run db:vacuum

Or connect to the database directly:

sqlite3 local.db "VACUUM;"

Run this once a month if you’re an active user. For most users, it’s optional.

# Check local.db file size
ls -lh local.db # macOS/Linux
dir local.db # Windows

A healthy database with 5,000 articles typically stays under 50MB.

Every feed is fetched every 30 minutes. With 100 feeds, that’s 100 HTTP requests per cycle. Consider:

  • Removing feeds you haven’t read in months
  • Consolidating overlapping feeds (e.g., if you follow both a blog and its newsletter)
  • Adjusting the refresh interval for low-priority feeds

SparkFeed stores articles indefinitely by default. To limit growth:

# .env: keep only the last 90 days of articles
ARTICLE_RETENTION_DAYS=90

With this set, articles older than 90 days are automatically deleted during the cleanup job (except favorites, which are always kept).

For heavy workloads, you can tune SQLite’s behavior via pragmas. SparkFeed applies these optimizations by default:

PRAGMA journal_mode = WAL; -- Write-Ahead Logging: better concurrency
PRAGMA synchronous = NORMAL; -- Balance between safety and speed
PRAGMA cache_size = -64000; -- 64MB page cache in memory
PRAGMA foreign_keys = ON; -- Enforce referential integrity
PRAGMA temp_store = MEMORY; -- Temp tables in RAM, not disk

These are set automatically when the database connection is initialized. You don’t need to configure them manually.

The React frontend is already optimized, but here are some things to be aware of:

The article list uses virtual rendering. Only the articles visible in the viewport are rendered in the DOM. This keeps the list fast even with 10,000+ items.

All database reads are cached using TanStack Query. A repeated request (e.g., switching back to a feed you just viewed) returns the cached result instantly.

The production bundle is code-split per route, so only the code needed for the current view is loaded. The initial load is typically under 200KB gzipped.