Persistent Data Reference

Use usePersistentItem as the app's database hook. It is the persistence layer for user data that should survive reloads, sync across devices, and remain private to each user. Browser storage APIs such as localStorage, cookies, and IndexedDB are blocked in the sandbox.

Each app stores data in a user-private key-value JSON document. usePersistentItem reads and writes one key in that document.

usePersistentItem

Import the hook from the persistent item module:

import { usePersistentItem } from "@/hooks/use-persistent-item";

function Controls() {
  const [, setCount] = usePersistentItem<number>("count", 0);

  return (
    <div className="flex gap-2">
      <button onClick={() => setCount((current) => current - 1)}>-</button>
      <button onClick={() => setCount((current) => current + 1)}>+</button>
    </div>
  );
}

export default function App() {
  const [count] = usePersistentItem<number>("count", 0);

  return (
    <div className="space-y-4">
      <h2 className="text-2xl font-semibold">{count}</h2>
      <Controls />
    </div>
  );
}

Save Status and Errors

setValue updates local UI immediately. Persistence is queued and saved in the background. Use the metadata object when the app needs to show save state or recover from a failed save.

const [notes, setNotes, { status, error, retry, clearError }] =
  usePersistentItem<Note[]>("notes", []);

const addNote = (text: string) => {
  setNotes((current) => [
    ...current,
    { id: crypto.randomUUID(), text, createdAt: new Date().toISOString() },
  ]);
};

return (
  <div>
    {status === "saving" && <p className="text-muted-foreground">Saving...</p>}
    {error && (
      <div className="text-destructive">
        <p>{error.message}</p>
        <button onClick={retry}>Retry</button>
        <button onClick={clearError}>Dismiss</button>
      </div>
    )}
  </div>
);

Best Practices

  • Choose stable, descriptive keys such as tasks, settings, or generationHistory.
  • Use updater functions when the next value depends on the previous value.
  • Store dates as UTC ISO strings and parse them when displaying.
  • Persist small JSON state. For files, media, and large generated assets, store uploaded or generated URLs instead of raw binary or base64 data.
  • Keep temporary form state in React.useState, then write to usePersistentItem on submit.
  • Update persistent data from event handlers, AI hook callbacks, or completed workflows instead of mirroring every render through useEffect.
  • Share keys across components intentionally. Accidental key reuse can make unrelated components overwrite each other.
  • Use meta.status and meta.error for important save flows, but rely on automatic background persistence for ordinary interactions.