Autosave
Every change saves automatically. There is no Save button. The status indicator in the toolbar shows what state the save is in, every state has a precise meaning.
There is no Save button in sweetdocs. Every change you make goes through a per-document save queue that handles debouncing, retries, offline buffering, and conflict detection. The status indicator in the toolbar shows you exactly which state the queue is in.
How saves work
A single rule: when you stop typing for 1 second, the queue writes your latest content to the document row. Specifically:
- Every keystroke updates the in-memory content for the doc
- The same keystroke writes the content to
localStorageundersd:draft:{docId}so a crash can’t lose it - A 1-second debounce timer runs
- When it fires, the queue sends the content to the server
- The server’s response confirms with a new
updated_attimestamp - The localStorage draft is cleared
The whole thing is per-document, so editing two docs in two tabs doesn’t slow either of them down.
The status indicator
The small label next to the title shows what the queue is doing:
| Indicator | Meaning |
|---|---|
| Saved | Everything is durable in the cloud. |
| Saving… | A write is in flight (debounce fired or a request is pending). |
| Offline, will save when you reconnect | Your network is down. Edits are queued locally. |
| Save failed, retrying | A request failed. Exponential backoff in progress, up to 30 seconds between attempts. |
| Conflict, needs resolution | Another tab or device wrote to this doc since your last sync. See Conflict handling below. |
The label updates in real time as the state changes. Saved is the only durable state — when you see it, the change is safely in the cloud.
Offline editing
The queue checks navigator.onLine before every save attempt. When offline:
- Edits continue in the editor exactly as normal
- The status shows Offline, will save when you reconnect
- Writes are not retried until the browser reports
onlineagain - The localStorage draft keeps your latest content even if the tab is closed mid-offline
On reconnect, the queue automatically drains. Nothing for you to do.
Tab close / crash recovery
When you close a tab or navigate away, the queue runs a final flush using fetch keepalive — the browser delivers the last save even after the page is gone.
If something interrupts that (process kill, OS shutdown, browser crash), the localStorage draft holds your latest content. Next time you open the doc, the queue compares the draft against the server’s content; whichever is newer wins, and a save runs immediately if your draft was ahead.
Retries and backoff
When a save fails (network blip, server 5xx, transient error):
- Status flips to Save failed, retrying
- Next attempt runs after an exponential backoff
- Backoff caps at 30 seconds between attempts
- Successful save → backoff resets to zero
You don’t need to do anything during retries — the queue keeps pushing until it lands.
Conflict handling
The queue uses optimistic concurrency control keyed on the row’s updated_at timestamp. Every save sends “I think the row was last modified at X; only accept my write if that’s still true.”
If another tab or device wrote to the same doc since you loaded it, the server rejects with no row matched. The queue then:
- Re-fetches the current row from the server
- CRDT-backed docs: merges the server’s Yjs state into the local Y.Doc (Yjs handles divergence deterministically) and retries the save. No user-visible conflict — see Real-time collaboration for the merge model.
- Non-CRDT docs: flips status to Conflict and captures both versions. The user resolves which copy wins.
In practice, the CRDT path covers almost every case where multiple writers are live; the conflict path is reserved for the unusual case where the doc isn’t in a collab session (e.g., an offline write on a doc that nobody else has open).
Snapshot versions
Saves are every-keystroke (debounced). Versions are every-checkpoint. They serve different jobs:
- Save keeps your work durable.
- Version is a point-in-time snapshot you can browse and restore.
A new entry is added to document_versions when:
- You switch from Edit to Preview mode
- You leave the doc (close the tab, navigate to another doc)
The DB has a dedup trigger — identical content (the version you’d be creating matches the most recent one) gets dropped automatically, so you don’t end up with hundreds of identical snapshots. See Version history.
What this means in practice
- Type, look up, the indicator says Saved within ~1 second of stopping. Move on.
- Lose your wifi mid-paragraph: keep typing. The indicator says Offline. Reconnect, it flips back to Saving…, then Saved. Your paragraph is intact.
- Crash your laptop with unsaved edits: reopen the doc, your work is there from the localStorage draft.
- Open the same doc on a second device while editing on a first: both edits land via CRDT merge.
- Close the tab while a save is in flight: the keepalive flush delivers the last write.
There’s no scenario where you should reach for a Save button. There isn’t one.