Inventory what the browser currently owns
MDN documents localStorage as origin-partitioned browser storage that
persists after the browser closes. That means two users, browsers, devices, or
origins may hold different records. Before exporting anything, answer:
- Which origin and application version created the records?
- Which record types, keys, and relationships exist?
- Are identifiers stable or generated only in the browser?
- Which defaults or derived values are not stored?
- Can multiple tabs or users create duplicates?
- Which browser copy is authoritative, and who may decide that?
Freeze writes while taking the control export, or record exactly how changes during the migration will be reconciled. A successful export from one browser does not prove every expected record was captured.
Define the target and rollback boundary
Name the target database, environment, owner, import procedure, and account used for the exercise. Define what “durable” means for this application: backups, restore ownership, access control, retention, and operational support matter alongside the storage engine.
Write the rollback boundary before importing. It should answer:
- Can the target be cleaned without affecting another application?
- Will the old browser data remain read-only during acceptance?
- Who decides whether to retry, repair, or abandon the import?
- When may old local copies be removed?
Do not use a destructive database command copied from a generic guide. For
example, PostgreSQL's current pg_restore documentation
covers archives created by pg_dump, warns that restore executes source SQL,
and offers --single-transaction for all-or-nothing behavior. Those details
apply only when that archive and procedure are actually selected.
Freeze records, transforms, and rejection rules
Create a small migration specification before running the import:
| Source item | Target item | Transform | Reject when |
|---|---|---|---|
| Record identifier | Primary key | Preserve or map once | Missing or duplicate |
| Required text | Required column | Trim only if approved | Empty after transform |
| Status | Constrained value | Map from frozen table | Unknown value |
| Timestamp | Timestamp with zone | Parse with named assumption | Ambiguous or invalid |
Count each source record type and retain safe sample identifiers or checksums. Decide whether invalid and duplicate records stop the whole import or enter a review queue. Never silently discard them.
Import into an isolated target
Use the selected provider's current first-party procedure and retain the command, migration identifier, or provider receipt without credentials. Give the migration account only the permissions the procedure needs.
Lovable's current Supabase integration documentation describes connecting a customer-owned Supabase project and explicitly says there is no automatic migration between Lovable's built-in backend and a customer Supabase project. If that is your path, freeze the manual schema and data movement instead of assuming the connection moves existing records.
Capture both success and failure evidence. An import completion message shows that a tool finished; it does not establish record completeness, meaning, authorization, or application behavior.
Verify data and the critical workflow
Compare the target count with the accepted source count plus documented rejections. Then inspect samples chosen before the import, including edge cases such as the oldest record, newest record, missing optional value, and duplicate candidate.
Start a clean browser profile with local storage empty. Verify that:
- the expected records load from the target;
- a least-privileged test user sees only the intended scope;
- creating or editing one synthetic record writes to the target;
- refreshing or opening another clean session shows the change;
- the browser does not silently fall back to old local data; and
- a controlled dependency failure does not report false success.
Retain the observed result and evidence location for each check.
Accept, repair, or roll back
The data owner—not the migration script—decides whether the result is acceptable. Keep the old local data until that decision and the rollback window are explicit.
If verification fails, stop new writes, preserve the evidence, classify the gap as export, transform, import, authorization, or application behavior, and follow the prewritten cleanup path. Do not repeatedly import into an unknown partial state.
Evidence boundary for this draft
The accompanying dependency-free fixture ran five tests on August 4, 2026. It preserved control totals, applied one frozen title transform, rejected invalid and duplicate records, forced a clean-session reader to use only the target, and left the target unchanged after an interrupted migration.
That fixture used two synthetic records and no browser, provider account, cloud database, or approved dataset. This review version reports only those local results; a completed provider worksheet and data-owner decision are required before claiming that a real migration worked.
Migration worksheet
| Verification | Expected | Observed | Evidence |
|---|---|---|---|
| Target control count | |||
| Samples preserve meaning | |||
| Invalid and duplicate handling | |||
| Clean session uses target | |||
| Critical read/write workflow | |||
| Failure and cleanup path |
Record the old-data retention decision, rollback owner, and acceptance date beside this table.
Frequently asked questions
Can I just copy the localStorage JSON into a table?
Only after inventorying identifiers, types, relationships, invalid values, and authorization. A JSON import can complete while changing the meaning of data.
When should the browser copy be deleted?
After the data owner accepts target totals, samples, and clean-session behavior, and after the agreed rollback window. Until then, keep it protected from accidental writes.
Does a managed database make the application durable?
Not by itself. Durability also depends on owners, access, backups, restore testing, migrations, monitoring, and how the application handles failure.