Web Storage

Keep a draft in one tab

Use sessionStorage for an unfinished editor draft that belongs to one tab and disappears with its page session.

An editor draft sits in an awkward middle ground. Losing it on an accidental reload is painful. Keeping it forever, in every tab, turns it into application data it was never meant to be. sessionStorage is built for exactly this.

Same API, different scope

sessionStorage has the same methods as localStorage: setItem(), getItem(), removeItem(), strings only. What changes is the scope. It’s bound to the origin and to the top-level tab. A reload keeps the page session alive. Closing the tab ends it, and the data goes with it.

For our field-notes editor, we restore the draft when the page loads and save it on every keystroke:

const draftKey = 'field-notes:draft'
editor.value = sessionStorage.getItem(draftKey) ?? ''

editor.addEventListener('input', () => {
  sessionStorage.setItem(draftKey, editor.value)
})

Type a few words, hit reload, and the text is back. Open the Application panel under Session Storage and you see field-notes:draft with your text. Close the tab, open the app in a new one, and the entry is gone. That’s the behavior we wanted.

Saving on every input event is fine here because the value is one small string. If you were saving a larger document, you’d debounce it. For a note of a few hundred characters, don’t bother.

Tabs don’t share it

Open the app in a second tab while the first still has a draft. The second tab starts empty. Each tab has its own page session, so drafts never bleed between them. With localStorage you’d get the same half-written note in both tabs, and whichever tab saved last would win.

Duplicating a tab is the odd case. Some browsers copy the current sessionStorage into the duplicate as a starting point. After that the two diverge, and changes in one don’t appear in the other. It’s a copy, not a shared draft. Behavior differs slightly between browsers, so test the ones you support rather than assuming.

Clear it after saving

The draft exists to recover unsaved work. Once the note is saved to IndexedDB, the draft is stale:

await saveNote(note)
sessionStorage.removeItem(draftKey)

Skip this and a classic bug appears. The user saves, edits the saved note somewhere else, comes back to this tab, reloads, and the old draft overwrites their newer content. Or worse, the recovery banner keeps offering a draft that was already saved an hour ago. Remove the key right after a successful save and the problem never exists.

Walk the boundaries

Type a draft and reload: it survives. Open a second tab: empty. Close the first tab and reopen the app: gone. Write down where the draft appeared at each step. Then simulate a successful save, confirm the key is removed in DevTools, and reload once more to prove the editor starts clean.

Lesson completed