Build a Quick & Easy Instant Search User Interface using Alpine AJAX & Laravel
Photo by Markus Winkler on Unsplash
I’m going to walk you through how to easily add an instant search filter in your Laravel apps. When I say “instant search” I mean a text input that filters a list of results as you type. In this tutorial we’ll build out a basic contact list and then allow a user to search for a contact by name or email. We'll build the "search as you type" behavior, without writing any JavaScript, using the Alpine AJAX library. This UI pattern is one of my favorite examples that demonstrates the power and simplicity of Alpine AJAX.
The Data
You can start with a fresh Laravel install, get a database ready to go, then run php artisan migrate
in the terminal to generate the “users” table. To ensure that you’ve got user data to work with, you can run php artisan tinker
to enter the Tinker CLI, then User::factory()->count(20)->create();
to generate 20 users in your database.
The View
Since we're focusing on the user interface, let's start with the view so we can get a sense of how things will look and feel. We'll create a Blade template at resources/views/contacts.blade.php
. It’s going to be a basic page with a search form followed by a list of contacts. I’m leaving CSS styling out of this tutorial so that we can focus on writing good markup, style things however you’d like.
<!-- resources/views/contacts.blade.php -->
<!doctype html>
<html>
<head>
<title>Contacts</title>
</head>
<body>
<h1>Contacts</h1>
<form role="search" aria-label="Contacts">
<label for="term">Search</label>
<input type="search" id="term" name="term">
<button>Submit</button>
</form>
<h2>Results</h2>
<ul role="list">
@foreach($contacts as $contact)
<li>{{ $contact->name }} – {{ $contact->email }}</li>
@endforeach
</ul>
<body>
</html>
We've left the action
and method
attributes off of the search <form>
so by default the form will issue a GET
request to the current URL. The search <input>
has the name
“term” so that we can access the submitted search team on the backend. Next, we'll scaffold out some backend logic for this form.
The Route
Now let's create a new route at /contacts
. Our route logic will check if the incoming request contains a value for “term”, if so, it’ll query the “users” table for a record with a “name” or “email” that contains the search term:
// routes/web.php
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/contacts', function (Request $request) {
$contacts = User::when($request->term, function ($query, $term) {
$query->where(function ($query) use ($term) {
$query->where('name', 'like', "%{$term}%")->orWhere('email', 'like', "%{$term}%");
});
})->get();
return view('contacts', [
'contacts' => $contacts,
]);
});
Note that this is a simple search implementation just for demonstration purposes. You’d probably be better off using something like Laravel Scout for database searches, but that’s beyond the scope of this tutorial.
At this point you should be able to navigate to /contacts
in your browser, submit the search form, and see that the page reloads with an updated list of contacts. We've got our basic search form working! Now we can layer on extra features to make it feel really good to use.
The Interaction
It’s time to enhance our search form so that we get instant results as we type. This is where Alpine AJAX shines. Alpine AJAX is a small Alpine.js plugin that provides an easy way to make AJAX requests and render content on the page.
First we’ll get Alpine and Alpine AJAX installed; we’ll add two script tags in the page <head>
, but you can also install these libraries through NPM if you’d like:
<!-- resources/views/contacts.blade.php -->
<head>
<title>Contacts</title>
<script defer src="https://cdn.jsdelivr.net/npm/@imacrayon/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
</head>
Next, let’s add a few attributes to our search elements:
<!-- resources/views/contacts.blade.php -->
<h1>Contacts</h1>
<form role="search" aria-label="Contacts" x-init x-target="contacts">
<label for="term">Search</label>
<input type="search" id="term" name="term" @input.debounce="$el.form.requestSubmit()">
<button x-show="false">Submit</button>
</form>
<h2>Results</h2>
<ul id="contacts">
@foreach($contacts as $contact)
<li>{{ $contact->name }} – {{ $contact->email }}</li>
@endforeach
</ul>
Here's a breakdown of the attributes we've added:
-
x-init
on the search form initializes our Alpine component. -
id="contacts"
on the list of contacts allows our search form the target the list. -
x-target="contacts"
on the search form changes the behavior of the form: When it is submitted an AJAX request is issued to/contacts
and the updated<ul id="contacts">
returned in the response replaces the existing contact list on the page. -
@input.debounce
on the search input automatically submits the search form when the value of the input is changed. - Finally, we’ve added
x-show="false"
to the search form’s submit button. This is a small progressive enhancement that will ensure the search form stays usable with or without JavaScript. When JavaScript is loaded, the button is hidden, but if JavaScript fails to load, the button stays on the page and provides a way for the user to manually submit a search term.
That’s it! With the new Blade markup in place, refresh the page (make sure to clear out the ?term
query string in the URL if it's there). Now you should see the contact list magically update as you type.
Check out the Alpine AJAX examples page to see more UI examples in action. It’s wild to see all the things you can accomplish with such a small JavaScript library.
driesvints, imacrayon, jasko15modrac liked this article
Other articles you might like
How to add WebAuthn Passkeys To Backpack Admin Panel
Want to make your Laravel Backpack admin panel more secure with a unique login experience for your a...
Quickest way to setup PHP Environment (Laravel Herd + MySql)
Setting up a local development environment can be a time taking hassle—whether it's using Docker or...
How to set up Laravel Magic Link?
User authentication is crucial for making web applications secure and easy to use. Traditionally, pa...
The Laravel portal for problem solving, knowledge sharing and community building.
The community