Tauri のフロントエンドは普通の WebView なので、localStorage も IndexedDB もそのまま動きます。プラグインも権限設定も不要で、UI の一時状態や小さなキャッシュには最も手軽です。一方で実体は WebView が管理するフォルダに書かれ、消えるタイミングもアプリの都合とは無関係です。2 つの API の使い分けと、どこから tauri-plugin-store や SQL プラグインへ移すべきかを整理します。
前提条件
プラグインは不要です。IndexedDB を Promise で扱うために idb を入れます。
npm install idb
1. フロントエンドから実装する (TypeScript)
localStorage: 文字列のキーバリュー
// 値は文字列のみ。オブジェクトは JSON にする
localStorage.setItem('ui.theme', 'dark');
localStorage.setItem('ui.sidebar', JSON.stringify({ open: true, width: 240 }));
const theme = localStorage.getItem('ui.theme') ?? 'light'; // 無ければ null
const sidebar = JSON.parse(localStorage.getItem('ui.sidebar') ?? '{"open":true,"width":200}');
localStorage.removeItem('ui.theme');
API は同期で、呼び出しの間 UI スレッドが止まります。数百 KB の JSON を起動時に読むと初回描画が遅れます。
IndexedDB: 構造化データを非同期で
import { openDB, DBSchema } from 'idb';
interface NotesDB extends DBSchema {
notes: {
key: number;
value: { id?: number; title: string; body: string; updatedAt: number };
indexes: { 'by-updated': number };
};
}
const dbPromise = openDB<NotesDB>('notes-db', 1, {
upgrade(db) {
// 初回作成時と version を上げたときだけ呼ばれる
const store = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
store.createIndex('by-updated', 'updatedAt');
},
});
export async function saveNote(title: string, body: string) {
return (await dbPromise).put('notes', { title, body, updatedAt: Date.now() }); // 戻り値は id
}
export async function recentNotes(limit = 20) {
const all = await (await dbPromise).getAllFromIndex('notes', 'by-updated');
return all.reverse().slice(0, limit);
}
Uint8Array や Blob もそのまま保存できるので、サムネイルのキャッシュにも使えます。
使い分けの目安
| 観点 | localStorage | IndexedDB |
|---|---|---|
| 値の型 | 文字列のみ | オブジェクト、バイナリ、Blob |
| 容量 | 数 MB(WebView2 は 10MB 程度、WebKit は 5MB 程度) | 数百 MB 以上(ディスク残量に依存) |
| API | 同期 | 非同期(トランザクション) |
| 向く用途 | テーマ、最後に開いたタブ、UI 状態 | 一覧のキャッシュ、オフライン下書き、サムネイル |
2. バックエンドから実装する (Rust)
どちらも WebView 内部のストレージなので、Rust 側から読み書きする API はありません。Rust からも扱いたいデータは最初から Store プラグインや SQL プラグインに置きます。
動作確認
npm run tauri dev で値を保存し、アプリを閉じて再起動しても残っていれば成功です。実体は次の場所にあります(<identifier> は tauri.conf.json の identifier)。
- Windows:
%LOCALAPPDATA%\<identifier>\EBWebView\Default\配下のLocal Storage\leveldbとIndexedDB - macOS:
~/Library/WebKit/<identifier>/WebsiteData/配下(開発中は実行ファイル名で分かれることがあります) - Linux:
~/.local/share/<identifier>/配下に WebKitGTK がlocalstorage/databasesを作ります
よくあるエラーと対処法
- 本番ビルドにすると開発中のデータが見えない: 保存先はオリジン単位です。開発時は
http://localhost:1420などの devServer、本番はtauri://localhost(Windows はhttp://tauri.localhost)と別オリジンになるため、別領域に保存されます。バグではありません。 QuotaExceededError: localStorage の上限超過です。IndexedDB へ移すか、Store プラグインでファイルに書きます。openDBがVersionErrorという趣旨のエラー: 既存 DB より小さい version で開いています。version は下げられないので、数値を戻すか DB 名を変えます。upgradeが呼ばれずblockedが発火する: 別ウィンドウが同じ DB を古い version で開いたままです。blockingコールバックで接続を閉じるか、DB を開くウィンドウを 1 つに絞ります。
OS ごとの違いと注意点
- アンインストールで消える: Tauri の NSIS アンインストーラーには「アプリデータを削除する」チェックがあり、選ばれると
%LOCALAPPDATA%\<identifier>ごと消えます。macOS で.appをゴミ箱に入れた場合は残りますが、クリーナー系ツールが~/Library/WebKitを掃除することがあります。 - WebView の更新・リセットで消える: WebView2 ランタイムや WebKitGTK の更新で内部形式が変わると、まれに読めなくなります。ユーザーが「サイトデータの削除」に相当する操作をできるかも OS 次第です。
- 移行の判断基準: 「消えたら困る」「Rust からも読みたい」「複数ウィンドウで共有したい」「ファイルとしてユーザーに渡したい」のどれかに当てはまれば Store プラグイン へ。「検索・並べ替え・集計がある」「数万件以上ある」なら SQLite へ移します。ブラウザストレージは「消えても再生成できるもの」に限定するのが安全です。
