Every time you break away from a dense novel or a technical paper to look up an obscure word in a browser, your deep focus collapses.
You open Chrome to look up "susurrus" or "chthonic". Three tabs later, you’re reading Twitter notifications, scanning Reddit threads, or clicking through ad-heavy dictionary sites with slow layout shifts. The alternative isn't great either: existing desktop dictionaries are either outdated shareware or bloated Electron wrappers that quietly chew through 500MB of RAM just to query plain text.
I wanted a zero-friction, distraction-free companion that sat quietly in the background, opened instantly via a global keystroke, and worked 100% offline even on an airplane or in a cabin without Wi-Fi.
That frustration led to FanaBabel. Here is the story of how I engineered an offline-first vocabulary desktop application using Tauri 2, Rust, and React 19, and the architectural lessons learned along the way.
The Vision: True Local-First Speed
When you’re in a reading flow, latency is the enemy. A search interface should feel as fast as a native terminal: hit a shortcut, type two letters, get the definition, and jump right back to your book.
To make this work without relying on external APIs, the application needed to satisfy four hard constraints:
- Zero network dependency: All lexicon data must live on disk.
- Instant startup and low memory footprint: No half-gigabyte runtime overhead.
- Resilient search: Typo tolerance and fast prefix autocompletion for literary, archaic vocabulary.
- Clean data boundaries: The read-only dictionary data must never mix with writable user history.
The Architecture: Why Tauri 2 and Rust?
Choosing the desktop stack was straightforward. Electron was out of the question for a lightweight utility tool. Tauri 2 offered the native system integration and lean memory footprint of Rust, paired with the rapid UI iteration of modern web tooling.
┌──────────────────────────────────────────────────────────┐
│ React 19 Frontend (Vite) │
│ Search Input │ Virtual History List │ Word Detail │
└────────────────────────────┬─────────────────────────────┘
│ Tauri IPC
┌────────────────────────────▼─────────────────────────────┐
│ Rust Backend │
│ Normalization │ Prefix Match │ strsim Fuzzy │
└──────────────┬────────────────────────────┬──────────────┘
│ │
┌───────────▼────────────┐ ┌────────────▼───────────┐
│ Read-Only Bundle │ │ OS AppData Directory │
│ dictionary.sqlite │ │ user_history.sqlite │
│ (Wiktextract + WordNet)│ │ (Persistent lookups) │
└────────────────────────┘ └────────────────────────┘
1. Embedded SQLite with rusqlite
Instead of querying a cloud database or loading a massive 100MB JSON file into memory on startup, FanaBabel embeds a pre-compiled SQLite database via rusqlite.
SQLite handles indexed queries in single-digit milliseconds. More importantly, it keeps memory consumption flat regardless of whether the dictionary contains 50,000 or 500,000 words.
2. The React 19 + @tanstack/react-virtual Frontend
On the frontend, I used React 19, TypeScript, and Vite. When a user navigates through hundreds of past lookups in their search history, re-rendering massive DOM trees causes micro-stutters. Using @tanstack/react-virtual ensures only the visible elements are mounted, keeping navigation butter-smooth at 60 FPS.
Technical Hurdles & Engineering Decisions
Building an offline dictionary sounds deceptively simple until you confront lexical edge cases and data scale.
Challenge 1: The Build-Time Data Pipeline
Raw dictionary data is messy. I built a separate data-pipeline utility that ingests structured Wiktextract JSONL and WordNet datasets, normalizes definitions, extracts parts of speech, synonyms, and context examples, and compiles them into a clean SQLite schema.
The critical insight here was keeping the generation pipeline strictly separated from the application runtime. The desktop app does zero data transformations on startup; it simply opens a read-only handle to an already-indexed database bundled into the application resources.
Challenge 2: Separating Immutable Data from Mutable State
One of the fastest ways to corrupt an installed desktop app is writing user state into the application directory.
In FanaBabel:
-
The dictionary lives at
resources/dictionary.sqliteas a strictly read-only asset. -
User history is initialized and maintained in the platform's designated local application-data folder (
%APPDATA%on Windows,~/Library/Application Supporton macOS,~/.local/shareon Linux).
This cleanly decouples user search logs from application updates reinstalling or updating the dictionary never wipes out a reader's personal lookup log.
Challenge 3: Balancing Autocomplete with Fuzzy Matching
When readers encounter words like “phthisis” or “bourgeoisie”, misspellings are inevitable.
If a standard prefix search yields results, the app responds immediately. But when a query turns up empty, the Rust backend switches to an approximate matching pass using the strsim crate. Running fuzzy matching across a vast vocabulary can easily choke CPU threads, so the algorithm uses strict length heuristics and Levenshtein thresholds before triggering deeper similarity comparisons.
The result is instant feedback for common lookups, with a seamless fallback for misspelled literary terms.
The Takeaway
Building local-first desktop software in 2026 feels like a breath of fresh air.
By stepping away from client-server architectures and heavy web wrappers, FanaBabel launches in milliseconds, consumes less than 40MB of RAM, and never fails due to a dropped socket or a broken API key.
The project is completely open source:
- GitHub Repository: github.com/MasFana/FanaBabel
- Release Artifacts: Windows installers (NSIS/MSI) and portable binaries are live on the Releases page.
If you're interested in desktop software engineering, local-first architectures, or Rust-backed web apps, feel free to dive into the codebase, test the builds, or open an issue. I'd love to hear how you handle local data storage and offline search in your own projects.

Top comments (0)