Livewire Component for “Load More” Records

In this Tutorial, we will see how we can create a Livewire Component which will Load More Records from the Database when the User Scrolls to the bottom of the list. And we will do it properly without reloading all the Records when the Component re-renders.

Basically we need to Create a Pagination Component which will initially load all the items of the 1st Page. And then when the User Scrolls to the bottom of the Page, we will need to Load the items of the 2nd Page and append it to the bottom of the list. And so on till we reach the bottom of the list.

So I have a Item Model which I want to Paginate. I have the following Route defined in the routes/web.php

Route::get('/items', function() {
    return view('items');
});

And the View File, items.blade.php referenced above looks like below:

@extends('layouts.app')
 
@section('content')
    <h1 class="text-3xl font-extrabold text-gray-900 tracking-tight">Items</h1>
    <livewire:load-items perPage="10" page="1" /> 
@endsection

Nothing special in this Blade File, it references the Layout File using Template Inheritance Method (which seems to be quickly falling out of fashion). There is then a H1 Tag with some Tailwind Classes. And finally there is a Livewire Component which we will work upon now.

We can create the Livewire Component using the below Artisan Command.

php artisan make:livewire LoadItems

This will create 2 Files. Lets start with the Component File Located at app\Http\Livewire\Components

<?php
 
namespace App\Http\Livewire;
 
use Livewire\Component;
use App\Models\Item;
 
class LoadItems extends Component
{
    public $perPage;
    public $page;
 
    public function mount($page, $perPage) 
    {
        $this->page = $page ?? 1;
        $this->perPage = $perPage ?? 10;
    }
    public function render()
    {
        $results = Item::paginate($this->perPage, ['*'], null, $this->page);
        return view('livewire.load-items', [
            'results' => $results
        ]);
    }
}

Within the mount() we are setting the $page and $perPage Values. And within the render() we are paginating the Items and passing those Items to be displayed in the View.

Our View File of the Component is located at resources/views/livewire/load-items.blade.php and here we are just looping through the Items.

<div>
    <ul>    
    @foreach($results as $result)
        <li class="mt-4 border-b-2 p-4">{{$result->id}}: {{$result->name}}</li}>
    @endforeach
    </ul>
</div>

So at this stage if you go at our Route http://127.0.0.1/items, you will see first 10 items being displayed using Livewire Component.

Alright lets add a button at the bottom of this Component which will “Load More Records”.

<button wire:click="loadMoreItems" class="bg-red-500 px-4 py-2">
    Load More Items
</button>

Clicking this Button will result in a New Livewire Lifecycle and it will call loadMoreItems()as soon as the User performs a Click Action on it. loadMoreItems() will be called before the render() is called, so we will set the $perPage property to return 20 records instead of 10.

    public function loadMoreItems() 
    {
        $this->perPage += $this->perPage;
    }

So on 1st Click, render() will return 20 records to View. Next click will render 30 records to View and so on. At this stage we have successfully implemented the Pagination using the “Load More” Button.

However, truth be told, this is a horrible way of implementing the Functionality. On each click we are completely re-rendering all the items rather than returning the new items. Ideally, we should only be returning newer items on each Click. So lets see how we can fix this.

The way Livewire works by re-rendering the entire Component, it is not possible to return the New Records and append it to the View. We can overcome this drawback by creating another Component. We will call this Component “LoadMoreItems” and it will be responsible for Loading More Records on each Click.

We will remove the method loadMoreItems from LoadItems Component. And within the View we will remove the “Load More Records” Button display the “LoadMoreItems” Component there.

<livewire:load-more-items :page="$page" :perPage="$perPage" :key="'load-more-items-'.$page" />

This LoadMoreItems Component will conditionally return one of the following 2 Views:

  1. It will return its own View with “Load More Records” Button on the initial Load.
  2. On the 2nd Load, it will return the View of LoadItems Component with only the Items related to 2nd Page being passed to it.

So our LoadItems Component will look like below:

class LoadMoreItems extends Component
{
    public $page;
    public $perPage;
    private $loadMore = false;
 
    protected $listeners = ['loadMoreItems'];
 
    public function mount($page, $perPage) 
    {
        $this->page = $page ?? 1;
        $this->perPage = $perPage ?? 10;
    }
 
    public function loadMoreItems() 
    {
        $this->loadMore = true;
        $this->page += 1;
    }
 
    public function render()
    {
        if( $this->loadMore) {
            $results = Item::paginate($this->perPage, ['*'], null, $this->page);
            return view('livewire.load-items', [
                'results' => $results
            ]);
        } else {
            return view('livewire.load-more-items');
        }
    }
}

And our corresponding View is:

<button wire:click="$emitSelf('loadMoreItems')" class="bg-red-500 px-4 py-2">
        Load More Items
 </button>

Here is how this will work.

  1. On the Initial Load, User will see 10 Records from the LoadItems Component and then there will be a “Load More Items” Button which is displayed from the LoadMoreItems Component.
  2. And when the User Clicks on this Button an Event is Fired to the LoadMoreItems Component. This Event is handled by the loadMoreItems(). Here we just set Increment the Page Number and set the Flag loadMore to true.
  3. Now within the Render Method runs, we only return the items related to that particular Page. (Page Number 2 in this case). This items are then passed to the View of LoadItems Component and displayed in place of “Load More Items” Button, i.e. at the bottom of the list.
  4. Notice how a New “Load More Items” Button will now appear at the bottom of the list.

This way we have a “Load More” Livewire Component which works without re-rendering the entire records.

If you are interested in the code, you can check out all the files here.

If you are interested in Loading More Items when user Scrolls to the bottom, without needing Load More Button, you can check the files here. This uses a bit of Javascript using Hooks of Livewire.

Leave a Reply

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