Local Storage & Persistence
Most apps need to remember something between launches — a logged-in user's preferences, a
"don't show this again" flag, a cached list of items, or a small offline dataset. Because a
Cordova app's entire UI runs inside a WebView, you actually have three realistic
options for storing data on the device: the plain web localStorage API (already
built into the WebView, no plugin required), the more capable browser-native
IndexedDB, and a real embedded SQLite database via the
cordova-sqlite-storage plugin. This page walks through all three and helps you
decide which one fits a given job.
localStorage: The Simplest Option
Because Cordova apps run inside a standard WebView, the plain web localStorage API
works exactly as it does in a desktop browser — no plugin needed at all. It stores simple
key-value pairs as strings, persists across app restarts, and is scoped to your app's origin so
other apps can't read it. For settings, flags, small cached values, or a user's last-viewed tab,
it's the least amount of code you can write for a persistence need.
// Saving a simple setting
localStorage.setItem('themeMode', 'dark');
localStorage.setItem('lastSyncTime', Date.now().toString());
// Reading it back later (even after the app has been closed and reopened)
var theme = localStorage.getItem('themeMode');
console.log('Saved theme: ' + theme);
// Removing a single key, or clearing everything
localStorage.removeItem('lastSyncTime');
// localStorage.clear(); // wipes every key for this app
A slightly larger worked example — remembering whether a user has completed a first-run tutorial:
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
var hasSeenIntro = localStorage.getItem('hasSeenIntro');
if (hasSeenIntro === null) {
// First launch ever -- show the intro, then remember it was shown
showIntroScreen();
localStorage.setItem('hasSeenIntro', 'true');
} else {
showHomeScreen();
}
}
localStorage only stores strings (you must
JSON.stringify() objects/arrays before saving and JSON.parse() them back),
it has a practical storage ceiling of roughly 5-10MB depending on the platform and
WebView version, and it has no query language — you can only fetch a value by its exact key. It is
not a database; treat it strictly as a flat key-value store for small, simple data.Storing Objects and Arrays
Since every value is stored as a string, structured data needs to be serialized first. This is a common enough pattern that it's worth seeing explicitly:
var userProfile = {
name: 'Asha',
age: 29,
favorites: ['reading', 'cycling']
};
// Objects and arrays must be serialized to a string before saving
localStorage.setItem('userProfile', JSON.stringify(userProfile));
// ...and parsed back into a real object when you read it
var savedProfile = JSON.parse(localStorage.getItem('userProfile'));
console.log(savedProfile.name); // "Asha"
IndexedDB: A More Powerful Browser-Native Option
When you need to store more data than is comfortable in localStorage, or you need
to store and query structured records (not just flat key-value pairs), the WebView also gives you
IndexedDB — a transactional, object-oriented database built into the browser engine
itself, with no plugin required. It supports much larger storage quotas, indexes for fast lookups
by field, and asynchronous (non-blocking) reads and writes.
IndexedDB's API is lower-level and more verbose than localStorage —
you open a database, define "object stores" (roughly like tables), and work with transactions and
callback/promise-based cursors rather than simple getItem/setItem calls.
It's a deep topic on its own, so this page won't cover its full API in detail — the important
thing to know is that it exists as a built-in, no-plugin option when localStorage's
simple string-only model isn't enough, but you don't need genuine relational SQL queries either.
cordova-sqlite-storage: A Real SQL Database
When your data is genuinely relational — multiple related tables, the need to run WHERE,
JOIN, or aggregate SQL queries, or a dataset large enough that you want indexed,
structured storage — the cordova-sqlite-storage plugin gives your app an actual
embedded SQLite database, the same engine used natively on both Android and iOS. Install it like
any other Cordova plugin:
cordova plugin add cordova-sqlite-storage
Opening a database and running basic SQL only after deviceready has fired:
document.addEventListener('deviceready', onDeviceReady, false);
var db;
function onDeviceReady() {
// Opens (or creates) a local SQLite database file
db = window.sqlitePlugin.openDatabase({
name: 'myapp.db',
location: 'default'
});
db.transaction(function(tx) {
// CREATE TABLE -- only needs to run once, safe to call every launch with IF NOT EXISTS
tx.executeSql(
'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, body TEXT)'
);
// INSERT a row using bound parameters (never string-concatenate user input into SQL)
tx.executeSql(
'INSERT INTO notes (title, body) VALUES (?, ?)',
['Shopping list', 'Milk, eggs, bread']
);
// SELECT rows back out
tx.executeSql('SELECT id, title, body FROM notes', [], function(tx, results) {
for (var i = 0; i < results.rows.length; i++) {
var row = results.rows.item(i);
console.log(row.id + ': ' + row.title);
}
});
});
}
cordova-sqlite-storage JavaScript — but
this website's own project doesn't run a Cordova build, so you won't see it actually execute on this
page. Copy it into your own project's www/js/index.js to try it after installing the
plugin.Decision Guide: Which One Should You Use?
| Your Situation | Best Choice |
|---|---|
| A handful of settings, flags, or small cached strings | localStorage — simplest possible API, no plugin |
| A moderate amount of structured data, no SQL needed, want to stay plugin-free | IndexedDB — built-in, higher capacity, indexed lookups |
| Genuinely relational data, need real SQL queries (joins, filters, aggregates) | cordova-sqlite-storage — an actual embedded database |
| Large offline dataset you need to search/filter quickly | cordova-sqlite-storage — SQL indexes make this fast and predictable |
Combining Persistence With Offline Awareness
Persistence and network awareness go hand in hand. As covered on the
Network Information page, a well-built app should
detect when the device has lost connectivity and adjust its behavior — and one of the most useful
things it can do offline is fall back to locally cached data instead of showing
an empty screen or an error. A typical pattern: whenever a network request succeeds, save the
result to localStorage (or SQLite for larger datasets); when the app detects it's
offline, read from that local cache and show it immediately, perhaps with a small "showing saved
data" banner. This turns a hard failure into a graceful, still-useful experience.
Practical Tips
- Always wrap SQLite calls in the
devicereadyhandler — the plugin isn't available before then. - Wrap
JSON.parse()calls on stored data in atry/catch— corrupted or missing data shouldn't crash your app. - Don't store sensitive data (passwords, tokens) in plain
localStorage— it isn't encrypted. Look at a dedicated secure-storage plugin if you need that. - Keep your SQLite schema changes (adding columns, new tables) versioned with simple
CREATE TABLE IF NOT EXISTS/ALTER TABLEchecks run on each launch.