restate / v1.0.0

Save when you decide

Keep a store in IndexedDB or MMKV, load it once at startup, save explicitly or automatically, and show unsaved changes.

On this pageDeclare a saved storeWait once at startupSave, revert and resetShow unsaved changes and progressTry itSave automaticallyBefore the page closesHandle storage errors

Declare a saved store

Replace restate with persistedRestate and give it a key. Components keep using the same hook and set.

The saved value is restored automatically after creation. Editing changes memory; nothing is written until you save or turn on autoSave.

JavaScript / JSX
import { persistedRestate } from '@kkwaenggwari/restate';

export const useDraft = persistedRestate('', {
  key: 'article-draft',
  validate: (value) => typeof value === 'string',
});

Wait once at startup

At the client entry, import the app and its stores, then wait for restateManager.ready before rendering. Components can then read saved values directly.

Storage that is unavailable, such as during server rendering or in a blocked browser, is not a failure: those stores work in memory. ready rejects only when records cannot be read, migrated or validated.

JavaScript / JSX
import { createRoot } from 'react-dom/client';
import { restateManager } from '@kkwaenggwari/restate';
import App from './App';

async function start() {
  const root = createRoot(document.getElementById('root'));
  try {
    await restateManager.ready;
    root.render(<App />);
  } catch (error) {
    root.render(<p>Could not load saved data.</p>);
    console.error(error);
  }
}
start();

Save, revert and reset

save writes the current value, restore returns to the saved value, and resetAll clears both memory and storage. Each returns a Promise.

JavaScript / JSX
await useDraft.save(); // write the current value
await useDraft.restore(); // return to the saved value
await useDraft.resetAndClearStorage(); // initial value in memory, record removed

Show unsaved changes and progress

useStatus re-renders when the persistence status changes. dirty is true while memory differs from the value last loaded, saved or restored. operation names the running storage operation.

Pass a selector to re-render only when one field changes.

JavaScript / JSX
function SaveButton() {
  const { dirty, operation, error } = useDraft.useStatus();
  const saving = operation === 'save';

  return (
    <>
      <button disabled={!dirty || saving} onClick={() => useDraft.save().catch(() => {})}>
        {saving ? 'Saving…' : 'Save'}
      </button>
      {error != null && <p>Could not save your draft.</p>}
    </>
  );
}

Try it

Type, raise the storage latency and save. Replay the example to see the saved draft restored.

Preparing the example…

Save automatically

For settings and drafts that should always persist, opt in once. autoSave: true saves after each change; { delayMs } waits until changes pause.

A burst of changes becomes one write, and a change back to the saved value writes nothing. Failures appear in status().error. A component-owned scope writes a pending change when it unmounts.

JavaScript / JSX
export const useTheme = persistedRestate('light', { key: 'theme', autoSave: true });

export const useDraft = persistedRestate('', {
  key: 'article-draft',
  autoSave: { delayMs: 500 },
});

Before the page closes

Read the same status outside React. Warn before leaving with unsaved changes, or save everything when the page is hidden.

JavaScript / JSX
window.addEventListener('beforeunload', (event) => {
  if (useDraft.status().dirty) event.preventDefault();
});

window.addEventListener('pagehide', () => {
  restateManager.saveAll();
});

Handle storage errors

Storage errors reject the Promise of the operation. Every restate error has a code and, for saved stores, the key of the record.

JavaScript / JSX
try {
  await useDraft.save();
} catch (error) {
  if (error.code === 'STORAGE_UNAVAILABLE') showNotice('Saving is off in this browser.');
  else if (error.code === 'STORAGE_VALUE_TOO_LARGE') showNotice('This draft is too large.');
  else showNotice(`Could not save "${error.key ?? 'draft'}".`);
}

Search docs

Search titles, descriptions and text of every guide.