Persist Data Locally
Introduction
Section titled “Introduction”This guide covers the @parity/product-sdk-local-storage package, which provides a LocalKvStore for storing and retrieving key-value data between sessions. Use it for device-local data like saved preferences, unsubmitted drafts, or cached responses. Data is scoped to your Product and device; it does not sync across devices or leak between Products on the same Host.
Prerequisites
Section titled “Prerequisites”Before getting started, ensure you have:
- A Polkadot Product project running locally (see Set Up Your Project)
- Node.js 20 or later with ESM support (
@parity/product-sdk-local-storageis ESM only)
Install the SDK
Section titled “Install the SDK”The SDK installs two ways. The umbrella package, @parity/product-sdk, is the recommended starting point: one dependency that re-exports most of the SDK. Individual packages install only what you use, which keeps your bundle smaller and your dependencies explicit, so switch to them later as a bundle-size optimization. The two use different import specifiers, so switching styles means updating your imports. See Umbrella or Individual Packages for the full comparison.
-
Umbrella package: The whole SDK in one dependency.
Terminal window npm install @parity/product-sdk -
Individual package: Only local storage.
Terminal window npm install @parity/product-sdk-local-storage
The import specifiers differ between the two. The snippets in this guide use the standalone specifier @parity/product-sdk-local-storage; on the umbrella, the same exports come from the @parity/product-sdk/local-storage subpath. The React hooks shown later are umbrella-only, under @parity/product-sdk/react.
Initialize the Store
Section titled “Initialize the Store”createLocalKvStore() is an async factory. You must await it before performing any read or write, because the function connects to the Host storage backend before the store is usable.
import { createLocalKvStore } from '@parity/product-sdk-local-storage';import type { LocalKvStore } from '@parity/product-sdk-local-storage';
// Connects to the Host storage backend. Must run inside a Polkadot Product;// outside a host container this throws "Host storage unavailable".const store: LocalKvStore = await createLocalKvStore();createLocalKvStore() accepts an optional LocalKvStoreOptions argument. The supported option is prefix, which is covered in Namespace Keys with Prefixes.
Store String Values
Section titled “Store String Values”Use store.set(key, value) to write a string value for a key. Use store.get(key) to read it back. If the key has not been set, get returns null.
await store.set('network', 'paseo');
const network = await store.get('network');
if (network === null) { console.log('Key not found');} else { console.log('Network:', network);}Store JSON Values
Section titled “Store JSON Values”Use store.setJSON(key, value) to write any JSON-serializable value. The store handles serialization for you. Use store.getJSON<T>(key) to read it back; the generic type parameter T enables type-safe retrieval. If the key does not exist, getJSON returns null.
interface UserPreferences { theme: 'light' | 'dark'; language: string;}
await store.setJSON('preferences', { theme: 'dark', language: 'en',} satisfies UserPreferences);
const preferences = await store.getJSON<UserPreferences>('preferences');
if (preferences === null) { console.log('No preferences found');} else { console.log('Theme:', preferences.theme); console.log('Language:', preferences.language);}Remove Stored Values
Section titled “Remove Stored Values”Use store.remove(key) to delete a key. The method does not throw if the key does not exist, so you can call it safely without first checking whether the value is present.
await store.remove('network');Namespace Keys with Prefixes
Section titled “Namespace Keys with Prefixes”Within a Product, multiple features may share the same LocalKvStore. Prefixes partition the namespace further to avoid key collisions between those features.
Pass a prefix option to createLocalKvStore() to prepend prefix: to every key operated on by that store instance. For example, createLocalKvStore({ prefix: 'feature' }) stores the key 'setting' internally as 'feature:setting'.
import { createLocalKvStore } from '@parity/product-sdk-local-storage';
const settingsStore = await createLocalKvStore({ prefix: 'settings' });const cacheStore = await createLocalKvStore({ prefix: 'cache' });
// Stored internally as "settings:theme"await settingsStore.set('theme', 'dark');
// Stored internally as "cache:lastBlock"await cacheStore.set('lastBlock', '21540000');The Host-enforced Product-level namespace is separate from any developer-defined prefix. The Host's Product namespace is applied on top of your prefix, so a key 'setting' in a { prefix: 'feature' } store ends up stored as something like 'myproduct.dot:feature:setting', without you needing to construct that path yourself.
Use React Hooks
Section titled “Use React Hooks”If your Product is a React application, @parity/product-sdk provides hooks that integrate local storage directly into the React component lifecycle. The hooks read the current value on mount and re-render the component whenever it changes.
Wrap your application in ProductSDKProvider once at the root, then call the hooks anywhere inside the tree:
'use client';
import { ProductSDKProvider } from '@parity/product-sdk/react';import type { ReactNode } from 'react';
export function Providers({ children }: { children: ReactNode }) { return ( <ProductSDKProvider fallback={<div>Initializing SDK…</div>} > {children} </ProductSDKProvider> );}useLocalStorageString manages a plain string value. useLocalStorage<T> manages a JSON-serializable value typed by T. Both hooks return a tuple of [currentValue, setValue, { loading, error }]:
'use client';
import { useLocalStorage, useLocalStorageString,} from '@parity/product-sdk/react';
interface UserPreferences { theme: 'light' | 'dark'; language: string;}
function StorageDemo() { const [network, setNetwork, { loading: networkLoading }] = useLocalStorageString('network');
const [ preferences, setPreferences, { loading: prefsLoading, error: prefsError }, ] = useLocalStorage<UserPreferences>('preferences');
return ( <div> <p>Network: {networkLoading ? 'loading…' : (network ?? 'not set')}</p> <button onClick={() => setNetwork('paseo')}>Set network</button>
<p> Theme: {prefsLoading ? 'loading…' : (preferences?.theme ?? 'not set')} </p> <button onClick={() => setPreferences({ theme: 'dark', language: 'en' })}> Set preferences </button>
{prefsError && <p>Error: {prefsError.message}</p>} </div> );}To remove a key, access app.localStorage from useProductSDK() directly:
'use client';
import { useProductSDK } from '@parity/product-sdk/react';
function StorageActions() { const app = useProductSDK();
async function handleRemove() { await app.localStorage.remove('network'); }
return ( <div> <button onClick={handleRemove}>Remove "network"</button> </div> );}Limitations
Section titled “Limitations”- Storage is not synced across devices. Values written on one Host instance are not visible on another.
app.localStorageand everyLocalKvStoreare scoped to your Product. You cannot read or modify another Product's keys.- React hooks (
useLocalStorage,useLocalStorageString) are only available via the umbrella package@parity/product-sdk. The standalone@parity/product-sdk-local-storagepackage exposes no React hooks. createLocalKvStore()requires a Host backend. Running outside a host container, it throwsHost storage unavailable; there is no browserlocalStoragefallback. Run your Product inside Polkadot Desktop to use local storage. SeeHost storage unavailablein troubleshooting.
Where to Go Next
Section titled “Where to Go Next”-
Store Data on Chain
When data must outlive the device and be fetchable by anyone, move it to the Bulletin Chain.
-
Deploy Your Product
You've covered the capabilities; ship your Product to a live
.dotname with the Quick Start deploy routes. -
Product SDK API Reference
The full
product-sdksurface beyond this recipe: every package, class, and method.