Storage
@ionic/storage-angular wraps IndexedDB and SQLite into a key/value API for persistent app state. Works offline, larger than localStorage, available on iOS / Android / Web.
Setup, key-value, indexes, encryption
EXAMPLE
// 1) Install (Angular)
// npm i @ionic/storage-angular
// For SQLite on native: npm i @ionic-enterprise/secure-storage (paid)
// Or: ionic capacitor add ios; npm i @capacitor-community/sqlite
// 2) Register the module
// app.module.ts
import { IonicStorageModule } from '@ionic/storage-angular';
import { Drivers } from '@ionic/storage';
import { NgModule } from '@angular/core';
@NgModule({
imports: [
IonicStorageModule.forRoot({
name: '__myappdb',
driverOrder: [Drivers.IndexedDB, Drivers.LocalStorage],
}),
],
})
export class AppModule {}
// 3) Service wrapper
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage-angular';
@Injectable({ providedIn: 'root' })
export class StorageService {
private _store: Storage | null = null;
constructor(private storage: Storage) {}
async init() {
this._store = await this.storage.create();
}
async set(key: string, value: unknown) {
await this._store!.set(key, value);
}
async get<T>(key: string): Promise<T | null> {
return this._store!.get(key);
}
async remove(key: string) { return this._store!.remove(key); }
async clear() { return this._store!.clear(); }
async keys(): Promise<string[]> { return this._store!.keys(); }
async forEach(fn: (value: unknown, key: string) => void) {
return this._store!.forEach(fn);
}
}
// 4) Bootstrap before APP_INITIALIZER
// providers: [
// { provide: APP_INITIALIZER, multi: true, useFactory: (s: StorageService) => () => s.init(), deps: [StorageService] },
// ]
// 5) Use it
await this.storage.set('user', { id: 42, name: 'Ada' });
const user = await this.storage.get<User>('user');
// 6) Common patterns
// Auth token persistence
await this.storage.set('auth_token', token);
const saved = await this.storage.get<string>('auth_token');
if (saved) httpInterceptor.attach(saved);
// Offline cache
await this.storage.set(`feed:${userId}`, posts);
const cached = await this.storage.get<Post[]>(`feed:${userId}`);
if (cached) showImmediately(cached);
await refetch();
// Settings
await this.storage.set('settings', { theme: 'dark', fontSize: 16 });
// 7) When you need queries / large data — switch to SQLite
import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite';
const sqlite = new SQLiteConnection(CapacitorSQLite);
const db = await sqlite.createConnection('myapp', false, 'no-encryption', 1, false);
await db.open();
await db.execute(`CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY, body TEXT, ts INTEGER)`);
await db.run('INSERT INTO messages (body, ts) VALUES (?, ?)', ['hello', Date.now()]);
const { values } = await db.query('SELECT * FROM messages ORDER BY ts DESC LIMIT 50');
// 8) Encrypted storage (sensitive data)
// For tokens / health data: Capacitor's Secure Storage plugin (uses Keychain / KeyStore)
import { SecureStorage } from '@aparajita/capacitor-secure-storage';
await SecureStorage.set('auth_token', token, true /* sync to iCloud Keychain */);
const t = await SecureStorage.get('auth_token');
// 9) Don't use it for
// • Files / images → Filesystem API (Capacitor)
// • Large blobs → IndexedDB directly via idb-keyval
// • Cross-device sync → Firebase / Supabase / your backend
// 10) Best practices
// • Initialise once at app start (APP_INITIALIZER)
// • Wrap in a typed service so call sites don't sprinkle string keys
// • Encrypt sensitive values
// • Migrate schema on app upgrade (versioned keys)
Why it matters
Use Ionic Storage for app settings + small caches; reach for SQLite (Capacitor plugin) when you need queries; use Secure Storage for tokens. Different needs, different layers — don’t use localStorage for production iOS / Android apps (it gets cleared).
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
import { Preferences } from '@capacitor/preferences';
await Preferences.set({ key: 'token', value: 't0k3n' });
const { value } = await Preferences.get({ key: 'token' });
Try it Yourself »
Exercise
Capacitor key/value plugin.
import {
} from '@capacitor/preferences';
PascalCase.
Discussion
Loading…