惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
爱范儿
爱范儿
小众软件
小众软件
阮一峰的网络日志
阮一峰的网络日志
Recent Announcements
Recent Announcements
雷峰网
雷峰网
Last Week in AI
Last Week in AI
I
InfoQ
Google DeepMind News
Google DeepMind News
GbyAI
GbyAI
The Cloudflare Blog
aimingoo的专栏
aimingoo的专栏
量子位
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
D
DataBreaches.Net
S
SegmentFault 最新的问题
Hugging Face - Blog
Hugging Face - Blog
MyScale Blog
MyScale Blog
美团技术团队
V
V2EX
Jina AI
Jina AI
T
The Blog of Author Tim Ferriss
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
腾讯CDC
Vercel News
Vercel News
A
About on SuperTechFans
J
Java Code Geeks
Martin Fowler
Martin Fowler
V
Visual Studio Blog
博客园_首页
酷 壳 – CoolShell
酷 壳 – CoolShell
Recorded Future
Recorded Future
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
U
Unit 42
Engineering at Meta
Engineering at Meta
F
Fortinet All Blogs
Microsoft Azure Blog
Microsoft Azure Blog
P
Proofpoint News Feed
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
博客园 - 三生石上(FineUI控件)
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Adactio

June 16th, 2026, 11:12am June 16th, 2026, 10:16am Enhancing with CSS Grid Lanes How building an HTML-first site doubled our users overnight Speaking in Dublin June 13th, 2026, 9:09pm A tale of two browsers Sarah Canary by Karen Joy Fowler June 12th, 2026, 5:59pm The Field Guide to CSS Grid Lanes June 12th, 2026, 12:24pm June 12th, 2026, 8:58am June 11th, 2026, 6:22pm June 11th, 2026, 6:20pm June 10th, 2026, 6:25pm June 10th, 2026, 10:14am June 9th, 2026, 8:50pm June 9th, 2026, 1:55pm June 8th, 2026, 7:45pm June 8th, 2026, 4:32pm Amsterdamming June 5th, 2026, 3:16pm June 4th, 2026, 8:21pm June 2nd, 2026, 8:37pm 25 years of The Session Happy Monday everyone, and let's talk about gender and ethnicity ratios at tech events. AI and the Rise of Mediocrity May 28th, 2026, 7:24pm Picture at an exhibition May 27th, 2026, 9:41pm May 27th, 2026, 1:52pm May 25th, 2026, 8:03pm Gaeltacht cois Tamaise 2026 May 25th, 2026, 3:07pm May 23rd, 2026, 8:06pm May 23rd, 2026, 8:31am May 22nd, 2026, 3:38pm May 22nd, 2026, 8:19am May 22nd, 2026, 7:22am May 21st, 2026, 8:19pm Brigid by Kim Curran May 20th, 2026, 7:12pm The value is in the difficulty - Annotated May 17th, 2026, 6:21pm May 15th, 2026, 4:19pm Tito as Gaeilge The closing talks at UX London 2026 Three things about data Native Apps Should Be Avoided Whenever Possible — No One's Happy WebKit Features for Safari 26.5 May 11th, 2026, 4:17pm I knew my writing students were using AI. Their confessions led to a powerful teaching moment | Micah Nathan Gideon The Ninth by Tamsyn Muir May 8th, 2026, 3:55pm Better Browser Caching with No-Vary-Search May 7th, 2026, 9:55am May 7th, 2026, 7:49am The schedule for UX London 2026 Google’s Prompt API Reminder: You Can Stitch Together Lots of Little HTML Pages With Navigations For Interactions Netizen | Derek Sivers April 30th, 2026, 7:59pm April 29th, 2026, 8:12pm April 27th, 2026, 7:47pm April 25th, 2026, 12:03pm April 25th, 2026, 12:00pm April 25th, 2026, 8:03am April 24th, 2026, 7:57pm April 24th, 2026, 5:12pm Two Paradigms for Enhancing HTML Tags Summary punishment It's Not AI. It's FOMOnetization. Alistair Davidson / validation-enhancer · GitLab Dilation Expansion artifacts April 19th, 2026, 6:03pm Finn Mac Cool by Morgan Llywelyn April 17th, 2026, 7:58am April 16th, 2026, 6:42pm Threat models No-stack web development Design and Engineering, As One · Matthias Ott April 14th, 2026, 7:21am April 13th, 2026, 7:47pm April 11th, 2026, 8:39am April 10th, 2026, 5:36pm My salary history Conference organising in 2026 April 7th, 2026, 8:32pm TinyStart AI Might Be Our Best Shot At Taking Back The Open Web | Techdirt April 6th, 2026, 12:46pm The AI Great Leap Forward April 4th, 2026, 6:42pm April 3rd, 2026, 5:27pm April 2nd, 2026, 8:58pm April 2nd, 2026, 4:32pm Web Day Out - 12 March 2026 Mistrust HTML Video Poster Image: Enable Responsive Images and ALT Text for Poster
Never Lose Form Progress Again :: Aaron Gustafson
Aaron Gustafson · 2026-04-21 · via Adactio

Few things are more annoying than losing your progress halfway through a form. Maybe the browser crashes. Maybe the tab gets closed. Maybe your kid yells from the other room and you come back three hours later wondering why you ever thought now was a good time to fill out a mortgage application. Whatever the cause, form-saver makes those interruptions a lot less obnoxious. Which is nice, because forms are usually annoying enough on their own.

At its core, form-saver is a small web component that wraps a form, keeps an eye on it, stores values in localStorage, and restores them when the page loads again. Better yet, it clears out saved data after a successful submission so you’re not accidentally resurrecting stale information the next time someone stops by. Nobody wants yesterday’s half-finished support request shambling back to life.

Basic usage

All you need to do is wrap your form in the component:

<form-saver>
  <form action="/contact" method="post">
    <label>
      Name
      <input name="name" autocomplete="name" />
    </label>
    <label>
      Email
      <input name="email" type="email" autocomplete="email" />
    </label>
    <label>
      Message
      <textarea name="message"></textarea>
    </label>
    <button type="submit">Send</button>
  </form>
</form-saver>

That’s it. The component targets the first descendant form, saves values as users type or make changes, and restores them when they come back. No extra plumbing. Just a form with a slightly better memory than most of us have before coffee — depending on the day, that may not be a terribly high bar, but still.

This is especially handy for forms that are a little more involved than a simple email signup. Job applications, checkout flows, support requests, and multi-question onboarding forms all benefit from a little resilience. So do the people filling them out, who generally have better things to do than retype the same answers because a tab got squirrelly.

What actually gets saved?

form-saver supports the controls most of us reach for every day:

  • Text-style input fields
  • textarea elements,
  • select elements (including multi-selects), and
  • checkbox and radio controls.

File inputs are intentionally excluded.

Because the component works in light DOM, your form remains your form. Your labels, validation, layout, and CSS continue to work exactly as they did before. form-saver just adds a bit of memory and, ideally, cuts down on a few muttered curses.

Want to keep a few fields after submit?

In many cases, clearing everything after a successful submission is the right call. Sometimes, though, it makes sense to keep a few details around. Maybe you want to preserve a visitor’s name and email address on a contact form while clearing the message body. That way they do not have to keep retyping the boring bits. Nobody wakes up excited to enter their email address for the fourth time.

That is what the retain attribute is for:

<form-saver retain="name email">
  <form action="/contact" method="post"></form>
</form-saver>

After a successful submission, name and email stick around, but message gets cleared. Simple, sensible, and less likely to leave someone staring at your form like it just betrayed them personally.

Better yet, let users decide

Persisting form data can be incredibly helpful, but there is a human side to this too. Just because we can keep someone’s information around does not necessarily mean we should do it without asking. That is where retain-choice comes in. It lets you be useful without getting presumptuous.

Add it alongside retain and form-saver will inject an opt-in checkbox for the user. Nice and easy:

<form-saver
  retain="name email"
  retain-choice
  retain-choice-label="Store my contact information for later"
>
  <form action="/contact" method="post"></form>
</form-saver>

By default, that checkbox is inserted just before the first submit control. If the user leaves it unchecked, the retained fields are cleared along with everything else after submit. If they opt in, those selected fields remain. Their call, as it should be. Gotta love a little informed consent.

Need to place that control somewhere more appropriate in your layout? Use retain-choice-container to point to a CSS selector:

<form-saver
  retain="name email"
  retain-choice
  retain-choice-label="Remember my details next time"
  retain-choice-container=".form-footer"
>
  <form action="/contact" method="post"><div class="form-footer">
      <button type="submit">Send</button>
    </div>
  </form>
</form-saver>

That gives you a lot more control over the experience without making you build the retention UI yourself.

Need a custom storage key?

By default, form-saver derives its storage key from the wrapped form’s method and action, which is usually exactly what you want. It keeps different forms from stepping on one another and keeps the setup nice and boring. Boring is good.

If you need something more explicit, you can provide your own storage-key:

<form-saver storage-key="checkout:shipping-address">
  <form action="/checkout/shipping" method="post">
    <label>
      Street Address
      <input name="street-address" autocomplete="street-address" />
    </label>
    <label>
      Postal Code
      <input name="postal-code" autocomplete="postal-code" />
    </label>
    <button type="submit">Continue</button>
  </form>
</form-saver>

This is useful when a form’s URL is not stable or when you want multiple views to intentionally share the same saved state. Sometimes explicit is just easier. Sometimes it is the only way to stay sane.

Want to drive it yourself?

If you need more direct control, the component exposes a few methods:

const saver = document.querySelector("form-saver");

// Persist the current state
saver.saveFormState();

// Restore previously saved values
saver.restoreFormState();

// Clear out anything stored for this form
saver.clearSavedData();

That can be useful when you want to pair it with your own UI, analytics, or some custom workflow around save and restore. Or when you just like being the one driving and do not fully trust anything labeled “automatic.”

Progressive enhancement, as usual

This component follows a pattern I am always going to favor: start with a perfectly ordinary form, then layer on the enhancement. If JavaScript fails, the form still works. Users can still fill it out and submit it. They just will not get the recovery behavior. Annoying, perhaps, but not catastrophic. And that is very much the point.

That’s a pretty good trade-off.

And because saved values are only cleared after a successful submit flow, you do not lose everything just because client-side validation blocked submission or some other script got clever at exactly the wrong moment. That matters. A lot of “smart” form experiences are only smart right up until they are not.

Demo

If you want to kick the tires, I put together a live demo with examples of the retention options as well:

Grab it

The project is available on GitHub, and you can install it from npm:

npm install @aarongustafson/form-saver

If you want the easiest path, just import it and let the component register itself:

import "@aarongustafson/form-saver";

If you would rather define it yourself, you can import the class directly:

import { FormSaverElement } from "@aarongustafson/form-saver/form-saver.js";

customElements.define("form-saver", FormSaverElement);

Either way, you wind up with a more forgiving form experience and a little less needless frustration for the people filling it out. Which, in my book, is a pretty solid deal. The bar for delight in forms is often just “don’t make me do that again,” and honestly, I’ll take it.