Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Automatically save form drafts to localStorage as the user types. Restores on page reload with a toast notification. Clears on submit or reset.
The data-autosave attribute automatically persists form data to localStorage as the user types. If the page is accidentally closed or refreshed, the draft is restored on the next visit with a toast notification. The draft is cleared when the form is submitted or reset.
<form data-autosave="contact-form"> <input name="name" placeholder="Name"> <textarea name="message" placeholder="Message"></textarea> <button type="submit">Send</button></form>
Add data-autosave to any <form> element with a unique key as the value. The init script:
input and change event listeners on the formlocalStorage with the key vb-autosave:{key} and a timestampinput events so other enhancements updatetoast-msg if availabledata-autosave-init to prevent double-bindingPassword fields (type="password") and file inputs (type="file") are always skipped — sensitive data and file references are never stored.
Multi-select dropdowns (<select multiple>) round-trip correctly: every selected option is stored, and all matching options are reselected on restore.
| Attribute | Value | Description |
|---|---|---|
data-autosave |
string (optional) | Storage key for this form. When the value is empty (boolean form, e.g. <form data-autosave>), the key is derived from the form's method and action. |
data-autosave-retain |
space-separated field names | Allowlist of fields to persist. When set, only listed fields are saved and restored. Useful for opting out of sensitive or large fields. |
data-autosave-init |
boolean | Set automatically to prevent double-binding. Do not set manually. |
If you omit the value, the storage key is derived from the form's method and action. Two pages pointing at the same endpoint will share state, which is usually what you want.
<!-- Key derived as "post https://example.com/contact" --><form action="/contact" method="post" data-autosave> <input name="name" autocomplete="name"> <input name="email" type="email" autocomplete="email"> <button type="submit">Send</button></form> <!-- Equivalent explicit form --><form data-autosave="post https://example.com/contact">…</form>
Provide an explicit value when two forms on the same endpoint should not share drafts (e.g. an inline edit form and a full edit form).
Use data-autosave-retain to limit which fields are persisted. Useful when a form mixes lookup-friendly identity fields (name, email) with sensitive or transient content (a message body, a one-time code).
<form data-autosave="checkout" data-autosave-retain="name email"> <input name="name" autocomplete="name"> <!-- persisted --> <input name="email" type="email"> <!-- persisted --> <textarea name="message"></textarea> <!-- NOT persisted --> <input name="cvv" autocomplete="cc-csc"> <!-- NOT persisted --> <button type="submit">Pay</button></form>
Field names not in the retain list are skipped during both save and restore. Tightening the list later will not surface previously-saved fields that fall outside the new allowlist.
The draft is stored as a JSON object in localStorage. Each named field in the form becomes a key-value pair. A _ts timestamp is included for expiry checking.
<thead> <tr> <th>Detail</th> <th>Value</th> </tr> </thead> <tbody> <tr> <td>Key format</td> <td><code>vb-autosave:{<em>key</em>}</code></td> </tr> <tr> <td>Debounce</td> <td>500ms after last input</td> </tr> <tr> <td>Expiry</td> <td>24 hours from last save</td> </tr> <tr> <td>Skipped types</td> <td><code>password</code>, <code>file</code></td> </tr> </tbody> </table> </section> <section> <h2>Multiple Forms</h2> <p>Each form on a page must have a unique <code>data-autosave</code> key. The key is used directly in the <code>localStorage</code> key, so collisions between forms on different pages are avoided by using descriptive names.</p> <code-block language="html" show-lines label="Multiple forms with unique keys" data-escape><!-- Each form needs a unique key --><form data-autosave="signup-form"> <input name="email" type="email" placeholder="Email"> <input name="username" placeholder="Username"> <button type="submit">Sign Up</button></form> <form data-autosave="feedback-form"> <textarea name="feedback" placeholder="Your feedback"></textarea> <button type="submit">Submit</button></form>
Checkbox and radio inputs are handled correctly during both save and restore:
"on" when checked, "" when unchecked. On restore, the checked property is set accordingly.<form data-autosave="preferences"> <fieldset> <legend>Notifications</legend> <label> <input type="checkbox" name="email-notify" value="yes"> Email notifications </label> <label> <input type="checkbox" name="sms-notify" value="yes"> SMS notifications </label> </fieldset> <fieldset> <legend>Theme</legend> <label> <input type="radio" name="theme" value="light"> Light </label> <label> <input type="radio" name="theme" value="dark"> Dark </label> </fieldset> <button type="submit">Save</button></form>
When a draft is restored, input events are fired on each restored field. This ensures other Vanilla Breeze enhancements that listen for input events — such as data-count, data-grow, and data-show-when — update correctly.
<form data-autosave="registration" class="stacked"> <form-field> <label for="reg-email">Email</label> <input type="email" id="reg-email" name="email" required> </form-field> <form-field> <label for="reg-name">Full Name</label> <input type="text" id="reg-name" name="fullname" required> <small slot="help">As it appears on your ID.</small> </form-field> <form-field> <label for="reg-bio">Bio</label> <textarea id="reg-bio" name="bio" data-count="200" data-grow></textarea> </form-field> <button type="submit">Register</button></form>
When a draft is restored, a toast notification is displayed if toast-msg is available on the page. The toast uses the following configuration:
infoIf toast-msg is not present, the draft is still restored silently without a notification.
The autosave draft is automatically cleared in two scenarios:
localStorage when the form is submittedform.reset()The autosave attribute itself has no visual output. The only visible indicator is the toast notification on restore, which inherits styles from toast-msg.
/* The toast notification uses toast-msg if available *//* Customize the restore notification via toast-msg styles */toast-msg::part(toast) { --toast-bg: var(--color-surface-raised); --toast-color: var(--color-text);}
All behavior is gated on data-autosave-init. Without JavaScript, the form works normally — data simply is not persisted to localStorage.
toast-msg's accessible live regioninput events on restore ensures dependent enhancements (character counts, conditional fields) are in the correct state for assistive technology