Source Code

<div class="demo">
  <form id="settingsForm" class="settings-form">
    <div class="f-row">
      <label for="fName">Display name</label>
      <input id="fName" name="name" type="text" value="Alex Rivera" />
    </div>
    <div class="f-row">
      <label for="fEmail">Email</label>
      <input id="fEmail" name="email" type="email" value="alex@company.com" />
    </div>
    <div class="f-row">
      <label for="fRole">Role</label>
      <select id="fRole" name="role">
        <option value="viewer">Viewer</option>
        <option value="editor" selected>Editor</option>
        <option value="admin">Admin</option>
      </select>
    </div>
    <div class="f-row f-checkbox">
      <label><input type="checkbox" id="fNotify" name="notify" checked /> Email notifications</label>
    </div>

    <button type="submit" class="save-btn" id="saveBtn" disabled>No changes to save</button>
  </form>

  <div class="diff-panel" id="diffPanel" hidden>
    <h3>Review changes</h3>
    <ul class="diff-list" id="diffList"></ul>
    <div class="diff-actions">
      <button type="button" class="btn ghost" id="cancelDiff">Keep editing</button>
      <button type="button" class="btn primary" id="confirmDiff">Confirm &amp; save</button>
    </div>
  </div>
</div>

Form Change Diff Preview — Confirm Exactly What Will Change Before Saving

Form Change Diff Preview — Show Exactly What Will Change Before Saving · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Computes a genuine field-by-field diff against a snapshot of the form's original values
Correctly handles checkboxes, which FormData omits entirely when unchecked
Save button live-updates its label with an exact change count and disables itself when there is nothing to save
Confirmation panel shows old value struck through next to the new value for every changed field
Diff-friendly value formatting layer separates raw form values from what is actually displayed
Confirming a save correctly updates the baseline so a second edit cycle diffs against the latest saved state
Keep-editing cancel path leaves the form untouched and simply closes the review panel

About this UI Snippet

Form Change Diff Preview — Reviewable Confirmation Before You Save

Screenshot of the Form Change Diff Preview — Show Exactly What Will Change Before Saving snippet rendered live

Most settings forms save silently — click submit, and whatever the form currently contains overwrites whatever was there before, with no chance to review exactly what changed. That's fine for a single text field, but risky for a form with several fields where a user might not remember which value they actually edited. This snippet computes a genuine diff between the form's original state and its current state, and only lets the save proceed after showing that diff for confirmation.

Snapshotting the baseline once, correctly

snapshotForm() runs exactly once, when the page loads, and captures every field's value into a plain object using FormData. The one non-obvious detail: FormData omits unchecked checkboxes entirely — an unchecked box simply doesn't appear as an entry — so the snapshot function explicitly reads form.notify.checked and records 'on'/'off' itself rather than trusting FormData to represent that field's absence-or-presence correctly. Get this wrong and an unchecked-then-rechecked box would never show up in the diff at all.

Diffing is deliberately dumb — and that's the point

computeDiff() does nothing clever: it loops over every key in the original snapshot and does a strict string comparison against the current value. There's no fuzzy matching, no attempt to interpret *meaning* — just "is this exact string different from that exact string." For a confirmation UI whose entire job is to be trustworthy, predictable beats clever; a user reviewing a diff needs to know it reflects reality with zero ambiguity.

The Save button as a live change counter

refreshSaveButton() runs on every input and change event, recomputing the diff and updating the button's label to read "Save 2 changes" or disabling it entirely back down to "No changes to save" the moment a user reverts a field back to its original value. This means the button is never a lie — if it says "no changes," there genuinely are none, and if it's disabled, submitting is correctly impossible rather than merely discouraged.

Confirming applies the new baseline, not just closes the panel

When the user clicks Confirm & save, the code doesn't just hide the diff panel — it calls Object.assign(originalValues, currentValues()), replacing the baseline with the just-saved values. This matters: without it, a second edit-and-save cycle would incorrectly diff against the *original* pre-first-save values instead of the form's actual last-saved state, showing changes that were already committed as if they were still pending.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant to explain why the checkbox snapshot needs special handling given how FormData represents unchecked boxes, and to discuss what would go wrong if the baseline weren't updated after a confirmed save. It's also worth asking for a version that diffs nested or repeated form fields (like a dynamic list of email addresses), or one that persists the diff-confirmation requirement only for specific "sensitive" fields while letting harmless ones save immediately.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a settings form in HTML, CSS, and vanilla JavaScript that requires reviewing a diff before saving — no framework, no external library.

Requirements:
- A form with at least a text input, an email input, a select dropdown, and a checkbox, each with a starting value.
- On page load, snapshot every field's original value into a baseline object. Handle the checkbox correctly given that FormData omits unchecked checkboxes as entries entirely — do not lose that field from the diff.
- On every input/change event, recompute a field-by-field diff between the current form values and the baseline, and update the Save button's label to show the exact number of pending changes (or disable it entirely with a "no changes" label when nothing differs).
- Submitting the form must not save immediately — instead, open a review panel listing every changed field, showing its old value struck through next to its new value.
- The review panel needs two actions: a cancel action that simply closes the panel without saving, and a confirm action that applies the save and updates the baseline to the newly saved values (so a second edit-and-save cycle diffs correctly against the latest state, not the original pre-first-save values).
- Do not let the confirm review panel open at all if there are no actual changes to show.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Edit one or more fieldsThe Save button updates live, counting how many fields differ from their original values and disabling itself entirely when nothing has changed.
  2. 2
    Click SaveInstead of submitting immediately, a diff panel opens showing every changed field with its old value struck through next to the new value.
  3. 3
    Review the diffEach row is field-by-field — exactly what will change, nothing summarized or hidden. Revert manually in the form if something looks wrong.
  4. 4
    Confirm or keep editingConfirm & save applies the change (wire your real API call into the confirmDiff click handler) and resets the baseline; Keep editing closes the panel without saving.
  5. 5
    Adapt the field listUpdate FIELD_LABELS and formatValue() to match your own form's fields and how each value should be displayed in the diff.

Real-world uses

Common Use Cases

ADMIN
Account & team settings forms
Let admins confirm exactly what will change before updating a user's role, email, or permissions — reduces accidental overwrites.
Multi-field profile editors
Any settings page with several independent fields benefits from a clear, reviewable summary before committing.
AUDIT
Change-sensitive configuration
Config panels where a wrong save is costly (billing settings, infra config) benefit from an explicit review step.
UX
Reducing accidental saves
Disabling Save entirely when nothing changed prevents no-op network requests and confusing "saved" toasts for identical data.
Related: Natural Language Date Input — Type
See the Natural Language Date Input — Type for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

FormData only includes checked checkboxes as entries — an unchecked box is simply absent from the FormData object rather than present with a false value. Reading form.notify.checked directly and recording "on"/"off" explicitly avoids silently losing that field from the diff.

The diff recomputes on every input/change event, so a reverted field simply drops out of the change list — if every field is reverted, the Save button disables itself again automatically.

The confirmDiff click handler is where you'd wire in your real API call. As written it updates the local baseline snapshot to simulate a successful save, so a second round of edits diffs correctly against the just-saved state.

Yes — add the field to FIELD_LABELS with its display name, and to formatValue() if it needs special formatting (like the role and notify fields do). The diff and snapshot logic already iterate generically over every field.

A confirmation UI needs to be exactly trustworthy — if it says a field changed, it must genuinely differ, byte for byte. Fuzzy comparisons would risk either hiding a real change or flagging a non-change, undermining the entire point of a review step.