---
title: "Never lose what people typed · Agent Wranglers"
url: https://agentwranglers.se/playbook/app-ui/forms/keep-user-input
updated: 2026-10-20
licence: "All rights reserved"
---

# Never lose what people typed

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Forms](https://agentwranglers.se/playbook/app-ui/forms)
- Rule: Keep every value people entered after a failed submit, a lost connection, an expired session or an accidental reload. Never clear a form unless it was saved.
- How: After a failed submit, leave the fields as they are, or render them again with the submitted values on the server. Save a draft of long forms, with more than 5 fields or any free-text area, to `sessionStorage` or the server on every change. Restore it on load. Never store passwords or card numbers in a draft. Clear the draft only after the server confirms the save.
- Check: A browser test fills a form with more than 5 fields, reloads the page and finds the values again. A second test submits with a 500 from the server, and every field still holds its value.

Example:

```ts
const DRAFT_KEY = `draft:${location.pathname}`;
const SECRET_FIELDS = new Set(['password', 'cardNumber']);

export function keepDraft(form: HTMLFormElement) {
  const saved = JSON.parse(sessionStorage.getItem(DRAFT_KEY) ?? '{}');
  for (const [name, value] of Object.entries(saved)) {
    const field = form.elements.namedItem(name);
    const isTextField = field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement;
    if (isTextField && field.type !== 'file') field.value = String(value);
  }
  form.addEventListener('input', () => {
    const values = Object.fromEntries([...new FormData(form)].filter(([name, value]) => typeof value === 'string' && !SECRET_FIELDS.has(name)));
    sessionStorage.setItem(DRAFT_KEY, JSON.stringify(values));
  });
}
export const clearDraft = () => sessionStorage.removeItem(DRAFT_KEY);
```

```python
@app.post("/invoices")
def create_invoice():
    try:
        invoice = InvoiceInput.model_validate(request.form.to_dict())
    except ValidationError as error:
        return render_template("invoices/new.html", values=request.form, errors=field_errors(error)), 422
    saved = invoices.create(invoice)
    return redirect(url_for("show_invoice", invoice_id=saved.id), code=303)
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/forms/keep-user-input`.

- Previous: [Put server errors on their fields](https://agentwranglers.se/playbook/app-ui/forms/server-errors-on-fields)
- Next: [Save each form only once](https://agentwranglers.se/playbook/app-ui/forms/single-submit)
- [Forms](https://agentwranglers.se/playbook/app-ui/forms)
