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

valentin_vranic's avatar

Livewire and AlpineJS modal load slowly

I have nested livewire components in foreach loop. Each nesting is an instance of AlpinJS modal from livewire screencasts. I'm using pagination with 25 items, and still while it loads all the nested components/modals is takes multiple seconds. And inside of each component there is another instance of AlpineJS modal, which makes it also slow.

However, I'm coming from JQuery world where the modal loads, gets instantiated on certain button click, and there is no overload in listing items in foreach.

Is there a possibility to solve it in Livewir, to make it fast too? I've tried with lazy attribute on the component, but without success.

Thanks in advance, Vale

0 likes
6 replies
LaryAI's avatar
Level 58

To address the issue of slow loading nested Livewire components and AlpineJS modals, you can optimize the performance by following these steps:

  1. Defer Loading of Nested Components: Use the wire:init directive to defer the loading of nested components until they are needed. This can help reduce the initial load time.

  2. Lazy Loading: Ensure that you are using the lazy directive correctly to load components only when they are needed.

  3. Optimize AlpineJS Modals: Instead of initializing all modals at once, initialize them on demand. This can be done by using AlpineJS's x-show directive to conditionally render the modal.

  4. Pagination: Ensure that your pagination is efficient and only loads the necessary data.

Here is an example of how you can implement these optimizations:

Livewire Component

// ParentComponent.php
public function render()
{
    return view('livewire.parent-component', [
        'items' => Item::paginate(25),
    ]);
}

Parent Component Blade View

<div>
    @foreach($items as $item)
        <div wire:init="loadNestedComponent({{ $item->id }})">
            @if($this->isComponentLoaded($item->id))
                @livewire('nested-component', ['item' => $item], key($item->id))
            @endif
        </div>
    @endforeach

    {{ $items->links() }}
</div>

Parent Component Class

// ParentComponent.php
public $loadedComponents = [];

public function loadNestedComponent($itemId)
{
    $this->loadedComponents[$itemId] = true;
}

public function isComponentLoaded($itemId)
{
    return isset($this->loadedComponents[$itemId]);
}

Nested Component Blade View

<div x-data="{ showModal: false }">
    <button @click="showModal = true">Open Modal</button>

    <div x-show="showModal" @click.away="showModal = false">
        <!-- Modal Content -->
    </div>
</div>

Explanation

  1. Defer Loading: The wire:init directive is used to defer the loading of the nested component until it is needed. The loadNestedComponent method sets a flag to indicate that the component should be loaded.

  2. Conditional Rendering: The isComponentLoaded method checks if the component should be rendered. This prevents all nested components from being loaded initially.

  3. AlpineJS Modal: The modal is initialized and shown only when the button is clicked, using AlpineJS's x-show directive. This ensures that the modal is not rendered until it is needed.

By following these steps, you can significantly improve the performance of your Livewire and AlpineJS modals, making the loading process faster and more efficient.

MohamedTammam's avatar

Just use alpine without Livewire to show the model. If you're using Livewire with live modifier it will wait till the request goes to the server and comes back.

You mentioned that you used lazy, I can't understand how you used it without seeing the code.

valentin_vranic's avatar

@MohamedTammam I'll try to demonstrate my workflow

number-filter.blade.php

@foreach($filteredWhitelist as $filtered)
                <livewire:call-reiteration.whitelist.number-filter-row :$filtered :key="$filtered->id"/>
@endforeach

number-filter-row.blade.php - modal

<x-dialog wire:model="showProofDialog">
    <x-assets.button
        class="btn-default btn-mini"
        target="manageProof"
    >
        {{ $buttonText }}
    </x-assets.button>
    <x-dialog.panel>
        <livewire:call-reiteration.whitelist.proof-management :$filtered/>
    </x-dialog.panel>
</x-dialog>

proof-management.blade.php - modal

<x-dialog wire:model="showPlayback">
        <x-dialog.panel>
            <livewire:call-reiteration.whitelist.playback :$proof :$securityHashForPlay
                                                          :key="md5($proof->id)"/>
        </x-dialog.panel>
    </x-dialog>

both dialogs are controlled by their own LW property, for show/hide And let's say I've tried the lazy as

<livewire:call-reiteration.whitelist.proof-management :$filtered lazy/>
Snapey's avatar

why would you want to load so many modals? Especially when you can easily have one modal per component and load it with the chosen data

valentin_vranic's avatar

@Snapey that's what I'm doing currently (at least I think so). I'm managing each components modal with a LW property, and it works well, but still on loading the whole page it first fills all the modals.

Right now I'm not sure that I understand it well, how to make it work to load data only on certain button click and not on page init. I've followed the screencasts for building a modal, but still, there were only 5-10 lightweight components

valentin_vranic's avatar

@Snapey the main issue for me, for what I'm looking solution for, is that for my list page where I have 25 records listen, and in each row, there is another modal which includes a livewire component, which loads a file, and that makes the overhead, that in list page loading, it start to load all the nested components too...

Please or to participate in this conversation.