docs/articles/reactjs-storage.md
Discover how to implement reactjs storage using localStorage for quick key-value data, then move on to more robust offline-first approaches with RxDB, IndexedDB, preact signals, encryption plugins, and more.
Modern ReactJS applications often need to store data on the client side. Whether you’re preserving simple user preferences or building offline-ready features, choosing the right storage mechanism can make or break your development experience. In this guide, we’ll start with a basic localStorage approach for minimal data. Then, we’ll explore more powerful, reactive solutions via RxDB, including offline functionality, indexing, preact signals, and even encryption.
localStorage is a built-in browser API for storing key-value pairs in the user’s browser. It’s straightforward to set and get items, making it ideal for trivial preferences or small usage data.
import React, { useState, useEffect } from 'react';
function LocalStorageExample() {
const [username, setUsername] = useState(() => {
const saved = localStorage.getItem('username');
return saved ? JSON.parse(saved) : '';
});
useEffect(() => {
localStorage.setItem('username', JSON.stringify(username));
}, [username]);
return (
ReactJS LocalStorage Demo
<input
type="text"
value={username}
onChange={e => setUsername(e.target.value)}
placeholder="Enter your username"
/>
Stored: {username}
);
}
export default LocalStorageExample;
Pros of localStorage in ReactJS:
Downsides of localStorage While localStorage is convenient for small amounts of data, it has certain limitations:
For “remember user preference” use cases, localStorage is excellent. But if your app grows complex with structured data, large data sets, or offline-first features, you might quickly surpass localStorage’s utility.
While localStorage is simple, it’s limited to string-based key-value lookups and can be synchronous for all reads/writes. For more robust ReactJS storage needs, browsers also provide IndexedDB, a low-level asynchronous API that can store larger amounts of JSON data with indexing.
LocalStorage:
IndexedDB:
When data shapes get complex with large sets of nested documents or you want offline sync to a server, RxDB can transform your approach to ReactJS storage. It stores documents in (usually) IndexedDB or alternative backends but offers a reactive, NoSQL-based interface.
import { createRxDatabase } from 'rxdb';
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
(async function setUpRxDB() {
const db = await createRxDatabase({
name: 'heroDB',
storage: getRxStorageLocalstorage(),
multiInstance: false
});
const heroSchema = {
title: 'hero schema',
version: 0,
type: 'object',
primaryKey: 'id',
properties: {
id: { type: 'string', maxLength: 100 },
name: { type: 'string' },
power: { type: 'string' }
},
required: ['id', 'name']
};
await db.addCollections({ heroes: { schema: heroSchema } });
// Insert a doc
await db.heroes.insert({ id: '1', name: 'AlphaHero', power: 'Lightning' });
// Query docs once
const allHeroes = await db.heroes.find().exec();
console.log('Heroes: ', allHeroes);
})();
Reactive Queries: In a React component, you can subscribe to a query via RxDB’s $ property, letting your UI automatically update when data changes. React components can subscribe to updates from .find() queries, letting the UI automatically reflect changes perfectly for dynamic offline-first apps.
import React, { useEffect, useState } from 'react';
function HeroList({ collection }) {
const [heroes, setHeroes] = useState([]);
useEffect(() => {
const query = collection.find();
// query.$ is an RxJS Observable that emits whenever data changes
const sub = query.$.subscribe(newHeroes => {
setHeroes(newHeroes);
});
return () => sub.unsubscribe(); // clean up subscription
}, [collection]);
return (
{heroes.map(hero => (
{hero.name} - Power: {hero.power}
))}
);
}
export default HeroList;
By using these reactive queries, your React app knows exactly when data changes locally (or from another browser tab) or from remote sync, keeping your UI in sync effortlessly.
RxDB typically exposes reactivity via RxJS observables. However, some developers prefer newer reactivity approaches like Preact Signals. RxDB supports them via a special plugin or advanced usage:
import { createRxDatabase } from 'rxdb/plugins/core';
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
import {
PreactSignalsRxReactivityFactory
} from 'rxdb/plugins/reactivity-preact-signals';
(async function setUpRxDBWithSignals() {
const db = await createRxDatabase({
name: 'heroDB_signals',
storage: getRxStorageLocalstorage(),
reactivity: PreactSignalsRxReactivityFactory
});
// Create a signal-based query instead of using Observables:
const collection = db.heroes;
const heroesSignal = collection.find().$$; // signals version
// Now you can reference heroesSignal() in Preact or React with adapter usage
})();
Preact Signals rely on signals instead of Observables. Some developers find them more straightforward to adopt, especially for fine-grained reactivity. In ReactJS, you might still prefer RxJS-based subscriptions unless you add bridging code for signals.
For more advanced ReactJS storage needs, especially when sensitive user data is involved, you might want to encrypt stored documents at rest. RxDB provides a robust encryption plugin:
import { createRxDatabase } from 'rxdb';
import {
wrappedKeyEncryptionCryptoJsStorage
} from 'rxdb/plugins/encryption-crypto-js';
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
(async function secureSetup() {
const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
storage: getRxStorageLocalstorage()
});
// Provide a password for encryption
const db = await createRxDatabase({
name: 'secureReactStorage',
storage: encryptedStorage,
password: 'MyStrongPassword123'
});
await db.addCollections({
secrets: {
schema: {
title: 'secret schema',
version: 0,
type: 'object',
primaryKey: 'id',
properties: {
id: { type: 'string', maxLength: 100 },
secretInfo: { type: 'string' }
},
required: ['id'],
encrypted: ['secretInfo'] // field to encrypt
}
}
});
})();
All data in the marked encrypted fields is automatically encrypted at rest. This is crucial if you store user credentials, private messages, or other personal data in your ReactJS application storage.
If you need multi-device or multi-user data synchronization, RxDB provides replication plugins for various endpoints (HTTP, GraphQL, CouchDB, Firestore, etc.). Your local offline changes can then merge automatically with a remote database whenever internet connectivity is restored.
| Characteristic | localStorage | IndexedDB | RxDB |
|---|---|---|---|
| Data Model | Key-value store (only strings) | Low-level, JSON-like storage engine with object stores and indexes | NoSQL JSON documents with optional JSON-Schema |
| Query Capabilities | Basic get/set by key; manual parse for more complex searches | Index-based queries, but API is fairly verbose; lacks a high-level query language | JSON-based queries, optional indexes, real-time reactivity |
| Observability | None. Must re-fetch data yourself. | None natively. Must implement eventing or manual re-check. | Built-in reactivity. UI auto-updates via Observables or Preact signals |
| Large Data Usage | Not recommended for large data (blocking, synchronous calls) | Better for large amounts of data, asynchronous reads/writes | Scales for medium to large data. Uses IndexedDB or other storages under the hood |
| Concurrency | Minimal. Overwrites if multiple tabs write simultaneously | Multiple tabs can open the same DB, but must handle concurrency logic carefully | Multi-instance concurrency with built-in conflict resolution plugins if needed |
| Offline Sync | None. Purely local. | None out of the box. Must be implemented manually | Built-in replication to remote endpoints (HTTP, GraphQL, CouchDB, etc.) for offline-first usage |
| Encryption | Not supported natively | Not supported natively; must encrypt data manually before storing | Encryption plugins available. Supports field-level encryption at rest |
| Usage | Great for small flags or settings |
If you’re looking to dive deeper into ReactJS storage topics and take full advantage of RxDB’s offline-first, real-time capabilities, here are some recommended resources:
RxDB Official Documentation
Explore detailed guides on setting up storage adapters, defining JSON schemas, handling conflicts, and enabling offline synchronization.
RxDB Quickstart
Get a step-by-step tutorial to create your first RxDB-based application in minutes.
RxDB GitHub Repository
See the source code, open issues, and browse community-driven examples that integrate ReactJS, Preact Signals, and advanced features like encryption.
RxDB Encryption Plugins
Learn how to encrypt fields in your collections, protecting user data and meeting compliance requirements.
Preact Signals React Integration (Example)
If you want to combine React with signals-based reactivity, check out example code and bridging approaches.
MDN: Using the Web Storage API
Refresh on localStorage basics, including best practices for small key-value data in traditional React apps.
With these follow-up steps, you can refine your reactjs storage strategy to meet your app’s unique needs, whether it’s simple user preferences or robust offline data syncing.