Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Kevin McKraken's avatar

Livewire: Refresh/Update DOM in ForEach?

I am having some trouble with updating a list of items, where the array is stored, and I can call it into a foreach loop to render out paragraph elements, but every time an item is added to the array, it keeps the elements that are already rendered, and adds the full, updated list to it. Is there a good way to refresh just that part of the DOM to show only what's currently in the list, and not stack it? I looked at the "Troubleshooting" page of the Livewire docs, but I'm not sure how the wire:key="name" thing works, or if it will work in this case. Weirdly, not a lot of information about it. I had considered using a child component, but I can't seem to get that to work correctly.

I have looked over Google quite a few times, but haven't had luck finding anything useful.

The code is a bit long, but hopefully it makes sense. Also, I'm still very new to this, so it's probably not the most optimal solution for what I want to accomplish.

TagSearchBar.php

<?php

namespace App\Http\Livewire;

use App\Models\Tag;
use Livewire\Component;

class TagSearchBar extends Component
{

    public $tagQuery;
    public $tagResult;
    public $tagSelected;
    public $tagSelectedList;

    protected $listeners = [
        'tagListUpdated' => 'updatedTagSelected'
    ];

    public function mount()
    {
        $this->resetSearch();
        $this->tagSelected = [];
        $this->tagSelectedList = [];
    }

    public function resetSearch()
    {
        $this->tagQuery = '';
        $this->tagResult = [];
    }

    public function updatedTagQuery()
    {
        $this->tagResult = Tag::where('alias', 'like', '%' . $this->tagQuery . '%')
        ->take(25)->get()->toArray();
    }

    public function addToTagList($tagId)
    {
        $this->tagSelected[] = $tagId;
        $this->emit('tagListUpdated');
    }

    public function updatedTagSelected()
    {
        foreach ($this->tagSelected as $tagId)
        {
            $this->tagSelectedList[] = Tag::find($tagId);
        }
        $this->render();
    }

    public function render()
    {

        return view('livewire.tag-search-bar', [
            'tags' => Tag::all(),
        ]);
    }
}

tag-search-bar.blade.php

<div class="tag-search-container relative">
    <div class="selectedTags">
        @foreach ($tagSelectedList as $selectedTag)
            <p
                class="tag-{{ $selectedTag['type'] }} tag-pill"
                title="{{ $selectedTag['type'] }}: {{ $selectedTag['slug'] }}"
                wire:click="removeFromTagList({{ $selectedTag['id'] }})"
            >
            {{ $selectedTag['alias'] }}
            </p>
        @endforeach
    </div>
    <input
        type="text"
        placeholder="Add tags..."
        value=''
        wire:model.debounce.500ms="tagQuery"
        wire:keydown.escape="resetSearch"
    >

    @if(!empty($tagQuery))
        <div class="tag-list-container absolute">
        @if(!empty($tagResult))
            @foreach($tagResult as $tag)
                <p
                    class="tag-{{ $tag['type'] }} tag-pill"
                    title="{{ $tag['type'] }}: {{ $tag['slug'] }}"
                    wire:click="addToTagList({{ $tag['id'] }})"
                >
                {{ $tag['alias'] }}
                </p>
            @endforeach
        @else
            <p>Add new tag?</p>
            <livewire:new-tag>
        @endif
        </div>
    @endif

</div>

In short, all of this code functions as intended, except the first foreach loop area. It renders the elements, but just stacks when updated. The code is also not complete, I have to add ways of preventing duplicates, and a remove function, I'm not worried about that right now.

0 likes
4 replies
undeportedmexican's avatar

I believe this is a DOM Diffing issue.

In short, livewire is losing track of what elements in the foreach it's supposed to update, so instead of updating/replacing them, it's just adding them at the end.

To cure this, you need to add a wire:key attribute inside your foreach to prevent that from happening, something like:

@foreach($tagResult as $i => $tag)
        <p
             class="tag-{{ $tag['type'] }} tag-pill"
             title="{{ $tag['type'] }}: {{ $tag['slug'] }}"
			 wire:key={{ $loop->index }} 
             wire:click="addToTagList({{ $tag['id'] }})"
        >
           {{ $tag['alias'] }}
        </p>
@endforeach

Here's a link to the part of the docs that talk about this https://laravel-livewire.com/docs/2.x/troubleshooting#dom-diffing-cures

2 likes

Please or to participate in this conversation.