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

> Discover how TypeWords leverages IndexedDB for robust local data persistence. Learn about its `idb-keyval` implementation and automatic migration from localStorage.

- Repository: [Zyronon/TypeWords](https://github.com/zyronon/TypeWords)
- Tags: deep-dive
- Published: 2026-09-03

---

**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`](https://github.com/zyronon/TypeWords/blob/main/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`](https://github.com/zyronon/TypeWords/blob/main/app/composables/practice-sentences/practice-sentence-cache.ts) and [`app/core/stores/setting.ts`](https://github.com/zyronon/TypeWords/blob/main/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`](https://github.com/zyronon/TypeWords/blob/main/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:

```typescript
{
  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`](https://github.com/zyronon/TypeWords/blob/main/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.

```typescript
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`](https://github.com/zyronon/TypeWords/blob/main/app/composables/practice-sentences/practice-sentence-cache.ts) (lines 6-19), this cache maintains:

```typescript
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`](https://github.com/zyronon/TypeWords/blob/main/app/core/stores/setting.ts).

### Test Data Storage

The file [`app/core/utils/word-test.ts`](https://github.com/zyronon/TypeWords/blob/main/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`](https://github.com/zyronon/TypeWords/blob/main/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`](https://github.com/zyronon/TypeWords/blob/main/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`](https://github.com/zyronon/TypeWords/blob/main/cache.ts).

### Where is sentence practice progress specifically saved?

Sentence practice data persists through **[`app/composables/practice-sentences/practice-sentence-cache.ts`](https://github.com/zyronon/TypeWords/blob/main/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.