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

RileyGWeb's avatar

Uncaught Snapshot missing on Livewire component with id:

(Before you comment, wire:key does not fix the issue)

I have these nested components that I want to be draggable between several areas:

@foreach($cards as $card)
    <livewire:card
        cardId="{{ $card->id }}" 
        wire:key="card-{{ $card->id }}" />
@endforeach
<div draggable="true" x-on:dragstart="dragStart($event)" x-on:dragend="dragEnd($event)" wire:key="card_{{ $cardId }}" data-card-id="{{ $cardId }}"  >
    <!-- removed irrelevant things -->
</div>
<script>
function dragStart(event) {
    event.dataTransfer.setData('card-id', event.target.getAttribute('data-card-id'));
}

function drop(event) {
    let cardId = event.dataTransfer.getData('card-id');

    Livewire.dispatch('updateCardOrder', cardId);
}
</script>

I get this console error:

Uncaught Snapshot missing on Livewire component with id: 3Q1CdoCOIA5JUDg4t8w2

And all of the cards in the component disappear, but the rest of the component remains intact. When I refresh the page, the changes have taken place (card is moved to new column).

Typically the fix for this is a wire:key from what I've seen, but as you can see I already have that. Any ideas on what else I can try? I can provide more of the components if needed. I even gave this a shot:

@foreach($cards as $card)
    <div wire:key="card-{{ $card->id }}">
        <livewire:card
            cardId="{{ $card->id }}"
            columnType="unlearned" />
    </div>
@endforeach
0 likes
14 replies
LaryAI's avatar
Level 58

The error message "Uncaught Snapshot missing on Livewire component with id" typically indicates that Livewire is trying to update a component that it no longer has a reference to. This can happen if the DOM has been manipulated outside of Livewire's knowledge, such as through JavaScript or other means.

Given that you are using drag-and-drop functionality, it's possible that the DOM manipulation involved in dragging an element is causing Livewire to lose track of the component's state.

Here are a few things you can try to resolve the issue:

  1. Ensure that you are using wire:key correctly. The wire:key should be unique for each instance of the component being looped over. It looks like you are already doing this, but double-check to make sure there are no duplicates.

  2. Since you are using Alpine.js for the drag-and-drop functionality, you might want to use Livewire's $wire magic property to interact with the Livewire component from your Alpine.js code. This ensures that Livewire is aware of the changes being made.

  3. When you dispatch the 'updateCardOrder' event, make sure that the Livewire component handling this event is updating its internal state correctly and that it's not causing the component to be removed from the DOM inadvertently.

  4. If you are manipulating the $cards collection on the backend after a card has been dragged and dropped, ensure that you are not resetting the keys of the collection, as this could cause Livewire to lose track of which components correspond to which data items.

  5. If none of the above solutions work, you might need to manually trigger a re-render of the Livewire component after the drag-and-drop operation is complete. You can do this by calling $this->emitSelf('refreshComponent') from within the Livewire component that needs to be updated.

Here's an example of how you might modify your JavaScript code to interact with Livewire:

function dragStart(event) {
    event.dataTransfer.setData('card-id', event.target.getAttribute('data-card-id'));
}

function drop(event) {
    let cardId = event.dataTransfer.getData('card-id');

    // Use Livewire's magic $wire property to call a method on the Livewire component
    Alpine.$wire.call('updateCardOrder', cardId);
}

And in your Livewire component, you would have a method like this:

public function updateCardOrder($cardId)
{
    // Perform the order update logic here

    // Then, re-render this component to reflect the changes
    $this->emitSelf('refreshComponent');
}

Remember to replace 'refreshComponent' with the actual name of the method you want to call to refresh the component.

If you continue to experience issues, please provide more details about the Livewire component handling the updateCardOrder event, as well as any other relevant code, so that we can further diagnose the problem.

RileyGWeb's avatar

@LaryAI $wire.call did not work. I have tried two other implementations - Dragula and Livewire Sortable - and no luck. I get some variation of an error telling me Livewire has lost track of what's going on. Right now with Livewire Sortable, I'm getting Component already registered on drag n drop.

Yes, wire:key on everything.

jigar_dhulla's avatar

I also need help in this regard. I am trying sortablejs.

My Livewire Blade file looks like:

<div x-data="{
        initSortable: function () {
            Sortable.create(this.$refs.sortableContainer, {
                animation: 150,
                onEnd: function (event) {
                    @this.call('updateOrder', Array.from(event.target.children).map((child) => child.id)) 
                } 
            })
        }
    }" 
    x-init="initSortable">
    <div class="table w-full" x-ref="sortableContainer">
        @foreach ($rules as $rule)
            <livewire:shortener-rule-row :key="$rule->id" :$rule />
        @endforeach
    </div>
</div>

Method to Update Order in Livewire Component looks like:

    public function updateOrder($ruleIds)
    {
        foreach ($ruleIds as $index => $id) {
            $rule = ShortenerRule::find($id);
            $rule->order = $index;
            $rule->update();
        }
    }

For me, it is not working only when moving any row to the last row. It is giving me same error: Uncaught Snapshot missing on Livewire component with id: BEnEDB3PC2HjpgHAqhje

RileyGWeb's avatar

@jigarakatidus After trying several drag and drop libraries, including some of alpine's features, it seems Livewire struggles to keep track of elements being dragged and dropped.

I'll tell you about a work around that is working for now - I switched to DragulaJs and simply added a wire:ignore to the column with the sortable items in it. I know that sounds bad, but theoretically, the database transactions should keep up with the dragging and dropping going on, so when I do a few drags and refresh, the changes are still updated.

It is certainly a temporary solution, but maybe it will help you for now.

2 likes
alanhesketh's avatar

@RileyGWeb

Did you ever find a long-term solution for this challenge? I've got a similar issue going on with the error message. When dragging items to the bottom of a list, they also just disappear... but only when dragging to the bottom.

Disciple's avatar

I've faced this problem recently also. [First reason] The problem is that you have wire:key on component: livewire:card cardId="{{ $card->id }}" wire:key="card-{{ $card->id }}" /> and also inside component wire:key="card_{{ $cardId }}" Try removing wire:key from the card component. [Second reason] Again, what I've just understood that one more reason why this is happening because of

{{-- views/components/facy-component.blade.php --}}
<div>
		<livewire:fancy-livewire-component/>
</div>

If you wrap the Livewire component inside div tag in some of blade component it also might happen. I just removed blade component and did everything in one, but I had to duplicate this, so the solution is not the best, but this is what it takes!

martin-kordas's avatar

For me, the error happened only with lazy components. The problem was in the Livewire component template which used another Blade component as a wrapper:

<x-my-component>
    ...
</x-my-component>

After wrapping it in standard HTML tag problem disappeared:

<div>
    <x-my-component>
        ...
    </x-my-component>
</div>
5 likes
EE's avatar

I am also working on nested components with sortable js and facing many weird issues but one of them was this and after spending many hours I just found the solution. Add empty div! (must be inside the loop)

@foreach($components as $key => $component)
  <div wire:key="{{$key}}" x-sortable-item="sort_{{$key}}">
      @livewire($component, key($key))
   </div>
   <div></div> {{-- DONT REMOVE EMPTY DIV THIS FIXES ONE OF THE WEIRD LIVEWIRE ISSUE--}}
@endforeach

UPDATE: I figured out the real issue! If statement should't be inside for each loop while loading Livewire component! I removed empty div and removed if statement then started works!

See more info here you can disable it with the following configuration in your application's config/livewire.php file: 'inject_morph_markers' => false,

tandogus's avatar

Thanks, removing the if statement worked for me

Koby's avatar

I also struggled with this for a while. What worked for me (using livewire:is in my case): remove if from the display loop as EE stated - gets rid of the error. But then, Livewire still snaps back the view so the dropped item gets right back to its original spot instead of landing where it should. Adding $this->skipRender() to finish off the update method solved it fully.

RileyGWeb's avatar

You guys might hate me for this but in the end, my solution was abandoning Livewire for vibe-coded Next.js in a completely new project from scratch. And it works fantastic lol.

Just tell it to write tests about everything it does and it won't become slopware.

casjonker's avatar

I think I might have run into a similar issue with the Alpine.js sort plugin. It seems to me that after dragging a new element within the parent of sortables, the element is added after a HTML comment node (which I assume are there to facilitate some @foreach magic). Because of this, livewire loses the connection to the element and throws the error. The solution for me was to register an onEnd method in the sortable config to make sure the element is droppen in between the @foreach comment nodes.

@script
<script>
    $js('dirtyAlpineSortLiveWireHotfix', ({ to }) => {
        let lastNode = to.lastChild
        for (let i = 0; i < to.childNodes.length; i++) {
            if (lastNode.nodeType === Node.COMMENT_NODE) {
                to.removeChild(lastNode);
                to.appendChild(lastNode);
                break;
            }
            lastNode = lastNode.previousSibling
        }
    });
</script>
@endscript
<div x-sort
	...
	x-sort:config="{
		onEnd: $js.dirtyAlpineSortLiveWireHotfix
	}"
>
	@foreach(..) 
		...
	@endforeach
</div>  	                          	

Please or to participate in this conversation.