Source Code

<div class="as-wrap">
  <div class="as-header">
    <h2 class="as-heading">New Comment</h2>
    <span class="as-status" id="asStatus"></span>
  </div>
  <form class="as-form" id="asForm">
    <input type="text" id="asSubject" class="as-input" placeholder="Subject" />
    <textarea id="asBody" class="as-textarea" rows="5" placeholder="Write your comment..."></textarea>
    <div class="as-actions">
      <button type="button" id="asClear" class="as-clear-btn">Clear draft</button>
    </div>
  </form>
</div>

Draft Autosave Form — Free HTML CSS JS Snippet

Draft Autosave Form · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Debounced autosave to localStorage 500ms after the user stops typing, using setTimeout/clearTimeout
Draft restored automatically on page load from localStorage, wrapped in try/catch for safety
"Draft saved" status indicator that fades in after each save and fades out automatically after 2 seconds
Clear draft button that empties both fields and removes the stored draft in one action
Debounce timer is cancelled if Clear draft is pressed mid-typing to avoid a save reviving cleared content
Single JSON object stores both subject and body under one localStorage key
Fully self-contained vanilla JS with no external dependencies

About this UI Snippet

Draft Autosave Form — Debounced localStorage Draft Saving

Screenshot of the Draft Autosave Form snippet rendered live

This snippet is a short comment composer (subject field plus textarea) that automatically saves what the user types to localStorage as they type, and restores it if they leave and come back.

Debounced saving

Every input event on either field calls scheduleAutosave(), which clears any pending setTimeout and starts a new one at DEBOUNCE_MS (500ms). This means saveDraft() only actually runs once typing pauses for half a second, rather than on every single keystroke — avoiding a write to localStorage on every character while the user is actively typing.

What gets saved and how

saveDraft() bundles both field values into one object and writes it with localStorage.setItem(STORAGE_KEY, JSON.stringify(draft)). On page load, restoreDraft() reads that key back with localStorage.getItem, wraps the JSON.parse call in a try/catch so a corrupted or missing value never throws, and — if valid — populates both fields from the parsed object.

Visible save feedback

After each successful save, a small green "Draft saved" status line fades in via a .as-visible class and fades back out automatically after 2 seconds, using a second timer that's cleared and restarted on every save so rapid typing doesn't cause the message to flicker on and off mid-sentence.

Clearing the draft

The "Clear draft" button empties both fields, cancels any pending debounced save, and calls localStorage.removeItem(STORAGE_KEY) so no stale draft is left behind to reappear on the next visit.

Step by step

How to Use

  1. 1
    Load the snippetClick "Draft Autosave Form" in the sidebar to load its HTML, CSS, and JS into the editor panels. The preview updates instantly.
  2. 2
    Edit the codeModify any panel — HTML, CSS, or JS. The preview refreshes as you type. Use Reset in each panel header to restore the original.
  3. 3
    Preview on devicesClick the Mobile (375px), Tablet (768px), or Desktop buttons in the preview header to check responsiveness.
  4. 4
    Export in your formatClick "HTML" to download a standalone file, "JSX" for a React component, "Tailwind" for a React + Tailwind CSS component, "Tailwind HTML" for a standalone HTML file with Tailwind CDN, "Vue" for a Vue 3 SFC with <template>/<script setup>/<style scoped>, or "Angular" for a standalone Angular .component.ts file. "Copy all" copies the full code to clipboard.
  5. 5
    Save your versionClick "Save as", type a name, and press Enter. Your snippet saves to IndexedDB and appears in the Saved tab.

Real-world uses

Common Use Cases

Comment and message composers
Protect users from losing an in-progress comment to an accidental tab close or refresh.
Reference for debounced localStorage writes
A clean, reusable debounce pattern applicable to any autosave-on-input feature.
Long-form editors and support tickets
Applies directly to email drafts, support ticket replies, or blog post editors.
Teaching debounce and persistence together
Shows how debouncing and localStorage combine to build a real autosave feature.

Got questions?

Frequently Asked Questions

Saving on every keystroke would mean a localStorage write per character typed, which is wasteful. Debouncing with setTimeout/clearTimeout ensures the save only happens once the user pauses typing for 500ms, batching rapid input into a single write.

restoreDraft() wraps the JSON.parse call in a try/catch. If parsing fails or the stored value is not an object, the catch block silently does nothing and the form simply starts empty instead of throwing an error.

No — each time it appears, a fresh 2-second hide timer is set and any previous hide timer is cleared first, so rapid saves reset the timer instead of stacking multiple fade-outs.

It clears the pending debounce timer first, then immediately empties both fields and calls localStorage.removeItem, so there is no delay and no risk of a stale scheduled save overwriting the cleared state afterward.