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

lucasweaver's avatar

x-show problem due to livewire v3

There seems to be an incompatibility when using x-show with livewire v3 due to how they handle modeling properties.

v3 now uses the old "wire:model.defer" behavior as default. But it seems alpine still models immediately.

Here's where this is a problem for me:

Let's say I have this:

< div x-data="{     
        topic: $wire.entangle('topic'),
        type: $wire.entangle('type')
">
<section x-show="!topic || !type">
<select id="type" wire:model="type">
<textarea id="topic" wire:model="topic">
<button wire:click="setTopicAndType">Set</button>
</section>

The problem is that in livewire, the 2 props only get populated on the button click (which is what I want), but in alpine, "topic" immediately gets modeled as soon as a user starts typing in the text area, which then makes the whole div disappear.

The only workaround I've found so far is to have a 3rd property called "topicAndTypeSet" and then make the visibility conditional upon that, but I really hate doing that.

Anyone have any suggestions?

0 likes
1 reply
LaryAI's avatar
Level 58

It sounds like you're encountering an issue where Alpine.js's x-show directive is reacting immediately to changes in your Livewire component's state, but you want it to wait until a certain action (like a button click) to reflect those changes.

One way to handle this is to use Livewire's $refresh magic action to manually control when Alpine should react to changes. You can refresh the component when you're ready for the Alpine component to update its visibility based on the new state.

Here's an example of how you might adjust your code:

<div x-data="{ topic: @entangle('topic'), type: @entangle('type') }" x-init="@this.on('updated', () => { $nextTick(() => topic = @js($this->topic)) })">
    <!-- Your input fields and other elements here -->

    <!-- Button to update Livewire component and Alpine state -->
    <button wire:click="updateTopicAndType">Update</button>
</div>

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

public function updateTopicAndType()
{
    // Perform your update logic here

    // Then emit an event to tell Alpine to update
    $this->emit('updated');
}

This way, you're using Livewire's event system to tell Alpine when to update, rather than having Alpine watch the Livewire state directly. The $nextTick function is used to ensure that the DOM is updated before Alpine tries to access the new values.

If you want to keep the x-show directive and have it react only after a certain action, you could use a separate piece of state to control the visibility:

<div x-data="{ topic: @entangle('topic'), type: @entangle('type'), showDiv: false }">
    <!-- Your input fields and other elements here -->

    <!-- Div that should be shown/hidden -->
    <div x-show="showDiv">
        <!-- Content that should be toggled -->
    </div>

    <!-- Button to toggle visibility -->
    <button x-on:click="showDiv = true">Show Content</button>
</div>

In this example, showDiv is a separate Alpine state property that you toggle with a button click, independent of the Livewire state. This gives you explicit control over when the div should be shown or hidden, without relying on the Livewire model updates.

Remember to adjust the logic to fit your specific use case, as the examples provided are based on the information given and might need to be adapted to your actual codebase.

Please or to participate in this conversation.