localStorage adapter

A client-side store — run the whole feedback loop in the browser, no server required.

@siteping/adapter-localstorage persists feedbacks in the visitor's browser. Combined with the widget's store option, you get a fully working feedback loop with zero backend — ideal for demos, prototypes, and docs sites (this site uses it).

npm i @siteping/adapter-localstorage
import { initSiteping } from "@siteping/widget";
import { LocalStorageStore } from "@siteping/adapter-localstorage";

initSiteping({
  store: new LocalStorageStore(),
  projectName: "my-demo",
});

Options

OptionTypeDefaultWhat it does
keystring"siteping_feedbacks"The single localStorage key holding the whole feedback array

Behavior notes

  • Data stays on one machine. Each visitor sees only their own feedback — by design. It never leaves the browser.
  • Corrupted data never crashes — and is never silently lost. A record missing its annotations — or its comments, as every record saved before threads does — reads with an empty list; an entry that can't be read — a missing field, an unknown type or status, a date that doesn't parse, comments that aren't a list — is skipped without hiding the others, and stored JSON that can't be parsed reads as an empty list. Before the next write replaces the key, whatever was skipped — those entries, or the whole blob when it isn't a JSON array — is appended to a JSON array under <key>.corrupt (e.g. siteping_feedbacks.corrupt), which the store never deletes. If that copy can't be saved, the write fails with a StorePersistenceError and the key is left as it was.
  • Quota pressure drops screenshots first. If a write exceeds the browser's storage quota (~5 MB per origin), the adapter retries once without the screenshot before giving up with a StorePersistenceError — the comment survives even when the image can't.
  • Bulk actions are safe within one tab. The store applies its writes one at a time, so selecting several feedbacks and resolving or deleting them together changes every one of them. The guarantee stops at the instance: two tabs — or two LocalStorageStores on the same key — each rewrite the whole array, and the last write wins.
  • Dates survive the round-trip — createdAt and friends come back as real Date objects, not strings.
  • SSR-safe: without localStorage, reads return an empty list and every write — clear() included — fails with a StorePersistenceError, never a raw exception.
  • Duplicate clientId submissions return the existing record, same as every other adapter.
Edit on GitHub

On this page