Building Inline Edit UI with Vue.js in Laravel Blade

📖 3 minutes read

Building Inline Edit UI with Vue.js in Laravel Blade

Inline editing is a great UX pattern – users can edit data right where they see it, without navigating to a separate form. Here’s how to implement it cleanly with Vue.js in a Laravel Blade template.

The Pattern

The key is tracking three states: viewing, editing, and saving. Here’s the basic structure:

new Vue({
    el: '#product-details',
    data: {
        editing: false,
        saving: false,
        form: {
            name: '{{ $product->name }}',
            price: '{{ $product->price }}',
            description: '{{ $product->description }}'
        },
        original: {}
    },
    methods: {
        startEditing() {
            // Store original values for cancel
            this.original = { ...this.form };
            this.editing = true;
        },
        
        cancelEditing() {
            // Restore original values
            this.form = { ...this.original };
            this.editing = false;
        },
        
        async save() {
            this.saving = true;
            
            try {
                const response = await axios.put(
                    '/api/products/{{ $product->id }}',
                    this.form
                );
                
                // Update local state with server response
                // This keeps UI in sync without page reload
                this.form = response.data.product;
                this.editing = false;
            } catch (error) {
                alert('Save failed: ' + error.response.data.message);
            } finally {
                this.saving = false;
            }
        }
    }
});

The Blade Template

<div id="product-details">
    <!-- View mode -->
    <div v-if="!editing">
        <strong>Name:</strong> @{{ form.name }}<br>
        <strong>Price:</strong> $@{{ form.price }}<br>
        <strong>Description:</strong> @{{ form.description }}
        
        <button @click="startEditing">Edit</button>
    </div>
    
    <!-- Edit mode -->
    <div v-else>
        <input v-model="form.name" placeholder="Name"><br>
        <input v-model="form.price" type="number" placeholder="Price"><br>
        <textarea v-model="form.description" placeholder="Description"></textarea>
        
        <button @click="save" :disabled="saving">
            @{{ saving ? 'Saving...' : 'Save' }}
        </button>
        <button @click="cancelEditing" :disabled="saving">Cancel</button>
    </div>
</div>

Key Points

  1. Preserve Original Values: When entering edit mode, clone the form data so cancel can restore it
  2. Update After Save: After successful save, update this.form with the server response. This keeps the UI in sync without refreshing the page
  3. Disable During Save: Disable buttons while saving is true to prevent duplicate submissions
  4. Handle Empty States: Show appropriate messaging when fields are empty (both in view and edit mode)

The Controller

public function update(Request $request, Product $product)
{
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'price' => 'required|numeric|min:0',
        'description' => 'nullable|string'
    ]);
    
    $product->update($validated);
    
    // Return the updated model
    return response()->json([
        'product' => $product->fresh()
    ]);
}

Why This Works

This pattern gives you:

  • Instant feedback (no page reload)
  • Clean cancel behavior (reverts to original values)
  • Server validation (with error handling)
  • Optimistic UI updates (form shows saved data immediately)

The secret sauce is updating this.form with the server response after save. This ensures your Vue state matches what’s actually in the database, without needing to reload the entire page.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *