Autocomplete Search Component using Livewire

Tailwind CSS New Livewire

Demo

This is a pro component. Get Livewiredemos pro access to view / download the component code.

Component Class


<?php

namespace App\Http\Livewire;

use App\Models\DummyProduct;
use Livewire\Component;
use Illuminate\Http\Request;
use Spatie\Searchable\Search;

class SearchComponent extends Component
{
    public $keyword;
    public $searchResults;
    public $resultCountToShow = 5;

    public function render()
    {
        return view('livewire.search-component');
    }

    public function updatedKeyword($keyword){
        $this->searchResults = (new Search())
            ->registerModel(DummyProduct::class, ['title', 'description'])
            ->search($keyword);
    }

}

Component View File


<div class="flex justify-center flex-1 lg:mr-32">
              <div
                class="relative w-full max-w-xl mr-6 focus-within:text-purple-500"
              >
                <div class="absolute inset-y-0 flex items-center pl-2">
                  <svg
                    class="w-4 h-4"
                    aria-hidden="true"
                    fill="currentColor"
                    viewBox="0 0 20 20"
                  >
                    <path
                      fill-rule="evenodd"
                      d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
                      clip-rule="evenodd"
                    ></path>
                  </svg>
                </div>
                <input
                  class="w-full leading-10 pl-8 pr-2 text-sm text-gray-700 placeholder-gray-600 bg-gray-100 border-0 rounded-md dark:placeholder-gray-500 dark:focus:shadow-outline-gray dark:focus:placeholder-gray-600 dark:bg-gray-700 dark:text-gray-200 focus:placeholder-gray-500 focus:bg-white focus:border-purple-300 focus:outline-none focus:shadow-outline-purple form-input"
                  type="text"
                  placeholder="Search for products"
                  aria-label="Search"
                  wire:model.debounce.300ms="keyword"
                  wire:keydown.enter="searchResults"
                />
                <button wire:click="searchResults" class="absolute inset-y-0 right-0 px-4 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-purple-600 border border-transparent rounded-r-md active:bg-purple-600 hover:bg-purple-700 focus:outline-none focus:shadow-outline-purple">
                    <i class="fas fa-search"></i>
                </button>
                <!-- Search Results -->
                <div class="relative" >
                    <div wire:loading.delay class="absolute top-100 mt-1 w-full border bg-white shadow-xl rounded text-center"><img src="/images/loading-buffering.gif" class="w-10 h-10 my-1 mx-auto"/></div>
                    @if(isset($searchResults) && !empty($keyword))
                    
                    <div class="absolute top-100 mt-1 w-full border bg-white shadow-xl rounded">
                    <div class="p-3">
                        @if($searchResults->count() > 0)
                        <div class="divide-y" x-ref="list">
                            @foreach($searchResults as $key => $searchResult)
                                @if ($key == $resultCountToShow)
                                    @break
                                @endif
                                <a href="{{$searchResult->url}}" class="p-2 flex block w-full rounded hover:bg-gray-100">
                                    <img class="rounded-full border-1 border-gray-300 w-8 h-8 p-1" src="{{$searchResult->searchable->getFirstMediaUrl('images')}}" />
                                    <span>{{$searchResult->title}}</span>
                                </a>
                            @endforeach                      
                        </div>
                        @elseif($searchResults->count() == 0)
                            <a href="#" class="p-2 flex block w-full rounded text-gray-700">
                            <span>No Results Found</span>
                            </a>
                        @endif
                    </div>
                </div>
                @endif
              </div>
              </div>
              
</div>

Usage

To include the search box on your page or navigation you just have to include the livewire component.


@livewire('search-component')

Documentation

In this demonstration we are using spatie searchable package for our search operation.

You must install it first.


composer require spatie/laravel-searchable

Preparing your Model

In this demonstration, I performed a search on a Model named DummyProduct. In order to search through models you'll have to let them implement the Searchable interface.

You'll only need to add a getSearchResult method to each searchable model that must return an instance of SearchResult. Here's how it could look like for a DummyProduct model.


    public function getSearchResult(): SearchResult
    {
         return new SearchResult(
            $this,
            $this->title,
            $this->link
         );
    }

Livewire Component code is simple, we have utilized the updated hook on the keyword variable. Thus whenever user type in the keyword, livewire components runs a query on the database to gather the results. We then run through the results on the frontend. Simple isn't it.