How TypeWords Uses IndexedDB for Local Data Persistence: A Technical Deep Dive

TypeWords uses the idb-keyval library with a custom versioning wrapper to store typed practice data, word lists, and user settings in IndexedDB, with automatic migration from legacy localStorage.

The open-source typing practice application TypeWords (zyronon/TypeWords) needs to reliably save user progress across sessions. Rather than using raw IndexedDB APIs directly, the project implements a clean abstraction layer that handles serialization, versioning, and backward compatibility. This article examines the actual implementation in the source code.

IndexedDB Wrapper Architecture

TypeWords builds its persistence layer on top of idb-keyval, a minimal promise-based wrapper around the browser's IndexedDB API found in the package dependencies. The core utilities live in app/core/utils/cache.ts, which exports reusable functions for reading and writing typed values.

Core Helper Functions

The foundation consists of two low-level helpers:

  • getLocal<T> – Retrieves and deserializes a value from IndexedDB
  • setLocal<T> – Serializes and stores a value as JSON (line 49 shows JSON.stringify(payload))

These wrap the underlying idb-keyval get and set methods and are imported across multiple modules including app/composables/practice-sentences/practice-sentence-cache.ts and app/core/stores/setting.ts.

Data Versioning and Metadata

Every cache definition includes a version field to prevent schema conflicts. For example, PRACTICE_WORD_CACHE.version = 2 (lines 8-11 of cache.ts). When reading cached data, the implementation checks this version and discards mismatched entries automatically.

The helper getLocalWithMeta<T> (lines 23-34) returns a structured object:

{
  val: T,           // The actual cached data
  updated_at?: number,  // Timestamp of last write
  version: number   // Schema version at time of write
}

This metadata enables the application to track cache freshness and trigger refreshes when data becomes stale.

Automatic Migration from localStorage

TypeWords handles legacy browser support through a transparent migration path. In app/core/utils/cache.ts (lines 226-239), the cache implementation:

  1. First attempts to read from IndexedDB
  2. Falls back to localStorage if no entry exists
  3. Automatically migrates any found localStorage entry to IndexedDB
  4. Removes the old localStorage key to prevent future fallbacks

This ensures users with older browsers continue working, and their data upgrades seamlessly on first access.

Domain-Specific Cache Implementations

The core wrapper powers several specialized caches for different practice modes:

Word Practice Cache (PRACTICE_WORD_CACHE)

Key: PracticeSaveWord

Stores the current word list, practice session state, and typing statistics.

import { getPracticeWordCacheLocal } from '@/app/core/utils/cache'

const cache = await getPracticeWordCacheLocal()
if (cache) {
  // Access word list, practice progress, accuracy stats
}

Article Practice Cache (PRACTICE_ARTICLE_CACHE)

Key: PracticeSaveArticle

Persists reading position within articles and associated performance metrics.

Sentence Practice Cache (PRACTICE_SENTENCE_CACHE)

Key: PracticeSaveSentence

Defined in app/composables/practice-sentences/practice-sentence-cache.ts (lines 6-19), this cache maintains:

await setPracticeSentenceCacheLocal({
  dictId: '123',
  items: [...],        // Array of sentence objects
  index: 5,            // Current position
  wrongIds: [],        // Missed sentences for review
  completedIds: [],    // Successfully typed sentences
  mode: 'normal',      // Practice mode setting
})

The implementation serializes this payload before storage (lines 44-49).

Additional IndexedDB Usage Patterns

Settings Persistence

User preferences including theme and language settings use the same IndexedDB helpers through app/core/stores/setting.ts.

Test Data Storage

The file app/core/utils/word-test.ts demonstrates simpler idb-keyval usage patterns for temporary test data.

Performance and Reliability Characteristics

The TypeWords IndexedDB implementation provides:

  • Asynchronous I/O – All operations return promises, preventing UI blocking
  • Automatic JSON serialization – Complex objects serialize transparently
  • Schema evolution safety – Version numbers invalidate incompatible cached data
  • Graceful degradation – localStorage fallback for unsupported environments

Summary

  • TypeWords uses idb-keyval as its IndexedDB foundation rather than native browser APIs
  • app/core/utils/cache.ts contains the versioned wrapper with metadata support and localStorage migration logic
  • Each practice mode (words, articles, sentences) has a dedicated cache with typed helpers
  • Automatic versioning prevents stale data from corrupting application state after updates
  • Transparent migration moves legacy localStorage data to IndexedDB without user intervention

Frequently Asked Questions

What library does TypeWords use for IndexedDB access?

TypeWords uses idb-keyval, a minimal promise-based wrapper around the IndexedDB API. This dependency appears in package.json and provides get and set methods that the project's custom cache utilities build upon.

How does TypeWords prevent data corruption when the app updates?

The cache system implements version checking. Each cache definition (like PRACTICE_WORD_CACHE) declares a version number. When reading data via getLocalWithMeta, the implementation compares the stored version against the expected version and discards mismatched entries, forcing a fresh fetch.

What happens to data stored in localStorage when IndexedDB becomes available?

TypeWords automatically migrates legacy data. On first access, if IndexedDB returns no entry but localStorage contains data, the value is copied to IndexedDB and the localStorage key is deleted. This happens transparently in lines 226-239 of cache.ts.

Where is sentence practice progress specifically saved?

Sentence practice data persists through app/composables/practice-sentences/practice-sentence-cache.ts, which exports getPracticeSentenceCacheLocal and setPracticeSentenceCacheLocal. This module uses the core IndexedDB helpers while adding domain-specific typing for sentence items, index tracking, and practice mode.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →