Skip to content

Sponsor

Sponsor

Real-World Examples, Best Practices, and API Reference

Example 1: User Preferences

javascript
const prefs = state({
  theme: 'light',
  fontSize: 16,
  sidebarOpen: true,
  notifications: true
});

autoSave(prefs, 'user-prefs', {
  sync: true  // Sync across tabs
});

// Effect applies preferences to the UI
effect(() => {
  document.body.dataset.theme = prefs.theme;
  document.body.style.fontSize = `${prefs.fontSize}px`;
});

// Toggle theme button
Elements.themeToggle.update({ onclick: () => { });
  prefs.theme = prefs.theme === 'light' ? 'dark' : 'light';
  // Saved automatically, synced to other tabs
};

Key patterns: sync: true for multi-tab consistency, effect-driven UI


Example 2: Form Draft Auto-Save

javascript
const draft = Forms.create({ title: '', body: '', tags: '' });

autoSave(draft, 'blog-draft', {
  debounce: 1000,  // Save 1 second after typing stops
  onSave: (data) => {
    // Only save the values, not errors/touched
    return data.values;
  },
  onLoad: (data) => {
    // Wrap back into the form format
    return { values: data };
  }
});

// Show "Draft saved" indicator
effect(() => {
  const exists = draft.exists();
  Elements.draftStatus.update({ textContent: exists ? 'Draft saved' : '' });
});

// Clear draft on publish
async function publish() {
  const result = await draft.submit();
  if (result.success) {
    draft.clear();  // Remove from storage
    draft.reset();   // Reset form
  }
}

Key patterns: debounce for typing, onSave/onLoad to transform data, clear on publish


Example 3: Shopping Cart with Expiration

javascript
const cart = Collections.create([]);

autoSave(cart, 'shopping-cart', {
  expires: 24 * 3600,  // Expire after 24 hours
  onError: (error, op) => {
    if (op === 'quota') {
      alert('Storage is full. Your cart may not be saved.');
    }
  }
});

// Cart persists across sessions (up to 24 hours)
cart.add({ id: 1, name: 'Widget', price: 9.99, qty: 1 });

// After 24 hours, the cart is automatically cleared on next page load

Key patterns: expires for time-limited persistence, onError for quota handling


Example 4: Session Data with sessionStorage

javascript
const session = state({
  lastPage: '/',
  scrollPositions: {},
  searchHistory: []
});

autoSave(session, 'session-data', {
  storage: 'sessionStorage'  // Cleared when tab closes
});

// Track page navigation
function navigateTo(page) {
  session.scrollPositions[session.lastPage] = window.scrollY;
  session.lastPage = page;
}

// Restore scroll position
function restoreScroll() {
  const pos = session.scrollPositions[window.location.pathname] || 0;
  window.scrollTo(0, pos);
}

Key patterns: sessionStorage for tab-scoped data, scroll position tracking


Example 5: Reactive Storage for Feature Flags

javascript
const flags = reactiveStorage('localStorage', 'features');

// Set feature flags
flags.set('darkMode', true);
flags.set('newUI', false);
flags.set('betaFeatures', true);

// React to flag changes
effect(() => {
  const darkMode = flags.get('darkMode');
  const newUI = flags.get('newUI');

  document.body.classList.toggle('dark', darkMode);
  document.body.classList.toggle('new-ui', newUI);
});

// Admin panel changes a flag → all effects re-run
flags.set('newUI', true);

Key patterns: reactiveStorage for key-value reactive data, effects on individual keys


Example 6: Watch for Authentication Changes

javascript
// Watch for auth token changes across tabs
const stopWatching = watchStorage('auth-token', (newToken, oldToken) => {
  if (newToken === null && oldToken !== null) {
    // Token was removed — user logged out in another tab
    window.location.href = '/login';
  } else if (newToken !== null && oldToken === null) {
    // Token was added — user logged in in another tab
    window.location.reload();
  }
}, {
  immediate: true  // Check current value on setup
});

// Clean up when component is destroyed
// stopWatching();

Key patterns: watchStorage for auth state, immediate for initial check


Example 7: Namespaced Multi-App Storage

javascript
// App 1: Blog editor
const blogState = state({ posts: [], currentDraft: '' });
autoSave(blogState, 'editor-state', { namespace: 'blog' });
// Stored as: 'blog:editor-state'

// App 2: Analytics dashboard
const analyticsState = state({ filters: {}, dateRange: '7d' });
autoSave(analyticsState, 'editor-state', { namespace: 'analytics' });
// Stored as: 'analytics:editor-state'

// Same key name, different namespaces — no collision

Key patterns: namespace to avoid key collisions between apps


Example 8: Manual Save/Load Control

javascript
const state = state({ data: [] });

// No auto-save — fully manual
autoSave(state, 'manual-data', {
  autoSave: false,
  autoLoad: false
});

// Load when user clicks
Elements.load.update({ onclick: () => { });
  if (state.exists()) {
    state.load();
    console.log('Data loaded');
  } else {
    console.log('No saved data');
  }
};

// Save when user clicks
Elements.save.update({ onclick: () => { });
  state.save();
  const info = state.storageInfo();
  console.log(`Saved (${info.sizeKB}KB)`);
};

// Clear saved data
Elements.clear.update({ onclick: () => { });
  state.clear();
  console.log('Cleared');
};

Key patterns: autoSave: false, autoLoad: false, manual save/load/clear


Best Practices

✅ Use namespaces to avoid key collisions

javascript
// ✅ Namespaced — safe
autoSave(state, 'settings', { namespace: 'myApp' });

// ❌ Generic key — might collide with other libraries
autoSave(state, 'settings');

✅ Use debounce for text inputs

javascript
// ✅ Saves once after typing stops
autoSave(formState, 'draft', { debounce: 500 });

// ❌ Saves on EVERY keystroke — too many writes
autoSave(formState, 'draft');

✅ Set expiration for temporary data

javascript
// ✅ Auto-cleans after 7 days
autoSave(state, 'cache', { expires: 7 * 24 * 3600 });

// ❌ Stays forever — clutters storage over time
autoSave(state, 'cache');

✅ Call destroy when done

javascript
// ✅ Clean up listeners and effects
function createWidget() {
  const state = state({ count: 0 });
  autoSave(state, 'widget');
  return {
    state,
    destroy: () => state.destroy()
  };
}

✅ Use onSave to filter sensitive data

javascript
// ✅ Don't store passwords or tokens
autoSave(userState, 'user', {
  onSave: (data) => {
    const { password, token, ...safe } = data;
    return safe;
  }
});

❌ Don't store non-serializable data

javascript
// ❌ Functions, DOM elements, Promises can't be serialized
const state = state({
  handler: () => {},        // Function
  element: document.body,   // DOM node
  promise: fetch('/api')    // Promise
});

// ✅ Only store plain data
const state = state({
  name: 'Alice',
  items: [1, 2, 3],
  settings: { theme: 'dark' }
});

Complete API Reference

autoSave(state, key, options?)

ParameterTypeDescription
stateReactive objectAny reactive state, ref, collection, or form
keyStringStorage key
optionsObjectConfiguration (see below)
ReturnsReactive objectThe same object with added methods

Options:

OptionTypeDefaultDescription
storageString'localStorage''localStorage' or 'sessionStorage'
namespaceString''Key prefix
debounceNumber0Delay in ms before saving
autoLoadBooleantrueLoad saved data on setup
autoSaveBooleantrueSave automatically on changes
syncBooleanfalseSync across tabs
expiresNumber/nullnullTime-to-live in seconds
onSaveFunction/nullnullTransform before saving
onLoadFunction/nullnullTransform after loading
onSyncFunction/nullnullCalled on cross-tab sync
onErrorFunction/nullnullCalled on errors

Added instance methods:

MethodReturnsDescription
.save()BooleanManual save
.load()BooleanManual load
.clear()BooleanRemove from storage
.exists()BooleanCheck if saved data exists
.stopAutoSave()ObjectPause auto-save
.startAutoSave()ObjectResume auto-save
.destroy()Clean up everything
.storageInfo()ObjectGet storage details

reactiveStorage(storageType?, namespace?)

ParameterTypeDefaultDescription
storageTypeString'localStorage'Storage type
namespaceString''Key prefix
ReturnsProxyReactive storage wrapper

Methods on the returned proxy:

MethodReturnsTriggers effects?
.set(key, value, opts)Boolean✅ Yes
.get(key)any/null❌ (registers dependency)
.remove(key)Boolean✅ Yes
.has(key)Boolean❌ (registers dependency)
.keys()Array❌ (registers dependency)
.clear()Boolean

watch(key, callback, options?)

ParameterTypeDescription
keyStringStorage key to watch
callbackFunction(newValue, oldValue) => {}
optionsObjectConfiguration
ReturnsFunctionDispose function

Options:

OptionTypeDefaultDescription
storageString'localStorage'Storage type
namespaceString''Key prefix
immediateBooleanfalseCall callback immediately

Utility Functions

FunctionReturnsDescription
ReactiveStorage.isStorageAvailable(type)BooleanTest if storage works
ReactiveStorage.hasLocalStorageBooleanIs localStorage available
ReactiveStorage.hasSessionStorageBooleanIs sessionStorage available

Available On

javascript
// Primary
ReactiveStorage.autoSave
ReactiveStorage.reactiveStorage
ReactiveStorage.watch
ReactiveStorage.withStorage        // Alias for autoSave

// ReactiveUtils
autoSave
withStorage          // Alias for autoSave
reactiveStorage
watchStorage         // Note: 'watchStorage', not 'watch'

// Globals (if state() is global)
autoSave
reactiveStorage
watchStorage

Load Order

html
<!-- 1. Reactive Core (required) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

<!-- 2. Other reactive modules (optional, load before storage) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

<!-- 3. Storage (only requires reactive core) -->
<script type="module">
  import { load } from 'https://cdn.jsdelivr.net/npm/dom-helpers-js@2.10.0/dist/dom-helpers.loader.esm.min.js';
  await load('reactive');
</script>

Only the reactive module is required (await load('reactive')). The storage module is fully standalone.


Congratulations!

You've completed the Reactive Storage learning path. You now understand:

  • ✅ What autoSave does and how it makes state persistent
  • ✅ How auto-load and auto-save work internally
  • ✅ All options: debounce, expiration, sync, namespaces, callbacks
  • ✅ All instance methods: save, load, clear, exists, destroy
  • ✅ Production features: throttle, circular refs, size warnings, quota handling
  • reactiveStorage for reactive key-value storage access
  • watch for monitoring specific storage keys
  • ✅ Cross-tab synchronization with loop prevention
  • ✅ The complete API reference

Your reactive state now persists like a pro!