Skip to content

Dirty Fields & PATCH Requests

Track which fields have changed since the last save and send only the modified data to the server. The save button is disabled when no changes have been made.

What this example covers:

  • Getting only changed fields with form.getDirtyFields()
  • Per-field dirty tracking with getFieldState().isDirty
  • Form-level dirty state with getFormState().isDirty
  • Disabling submit when nothing changed
  • Resetting defaults after save

Live Demo

JavaScript

getDirtyFields() returns an object with only the fields that differ from their defaults. After a successful save, call reset() with the current values to make them the new baseline.

js
Alpine.data('dirtyFieldsForm', () => ({
    result: '',
    showResult: false,
    form: Alpine.Form({ name: 'John', email: 'john@example.com', bio: '' }),
    handleSubmit() {
        const dirty = this.form.getDirtyFields();
        if (Object.keys(dirty).length === 0) {
            this.showResult = true;
            this.result = 'No changes to save';
            return;
        }
        this.showResult = true;
        this.result = 'Saving only changed fields:\n' + JSON.stringify(Alpine.raw(dirty), null, 2);
        this.form.reset({ ...Alpine.raw(this.form.getValue()) });
    },
}));

HTML

Each field label shows a "modified" badge when dirty. The submit button is disabled via :disabled="!form.getFormState().isDirty". A live preview of dirty fields is shown below the form.

html
<p style="color: #64748b; font-size: 14px; margin-top: 0">
    Edit some fields and submit. Only the changed fields are sent. Save button is disabled when
    nothing changed.
</p>

<div x-data="dirtyFieldsForm">
    <form @submit.prevent="handleSubmit()">
        <div class="field-group">
            <label>
                Name
                <span class="dirty-badge" x-show="form.getFieldState('name').isDirty"
                    >modified</span
                >
            </label>
            <input x-register:form="form.field('name')" type="text" />
        </div>
        <div class="field-group">
            <label>
                Email
                <span class="dirty-badge" x-show="form.getFieldState('email').isDirty"
                    >modified</span
                >
            </label>
            <input x-register:form="form.field('email')" type="email" />
        </div>
        <div class="field-group">
            <label>
                Bio
                <span class="dirty-badge" x-show="form.getFieldState('bio').isDirty">modified</span>
            </label>
            <textarea
                x-register:form="form.field('bio')"
                placeholder="Write something..."
            ></textarea>
        </div>
        <div class="btn-group">
            <button type="submit" :disabled="!form.getFormState().isDirty">Save Changes</button>
            <button type="button" @click="form.reset()">Discard</button>
        </div>
    </form>
    <pre x-show="showResult" x-text="result"></pre>
    <pre x-text="'Dirty fields: ' + JSON.stringify(form.getDirtyFields(), null, 2)"></pre>
</div>

How Dirty Tracking Works

isDirty compares against defaults — changing a value back to its original clears the flag:

js
// form starts with { name: 'John' }
form.setValue('name', 'Jane');
form.getFieldState('name').isDirty; // true

form.setValue('name', 'John');
form.getFieldState('name').isDirty; // false  (back to default)

Released under the MIT License.