Forms Required
Never lose what people typed
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 to do it
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.
Example
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);
@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)
How to 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.
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.