Updated June 2026. Tested on Laravel 13 and PHP 8.4. The old jQuery and Typeahead.js approach still works, but you do not need either anymore; this uses a small amount of plain JavaScript.
A search box that suggests results as you type makes a site feel quick and helpful. The shape is always the same: a Laravel endpoint that returns matching records as JSON, and a bit of front end code that calls it while the user types. We will build both, with no jQuery and no extra libraries.
The search endpoint
Add a route and a controller that returns matches as JSON. Laravel turns the collection into JSON for you.
// routes/web.php
Route::get('/search', [SearchController::class, 'find']);
use App\Models\User;
use Illuminate\Http\Request;
class SearchController extends Controller
{
public function find(Request $request)
{
$term = $request->string('q')->trim();
if ($term->length() < 2) {
return response()->json([]);
}
return User::query()
->where('name', 'like', "%{$term}%")
->orWhere('username', 'like', "%{$term}%")
->limit(8)
->get(['id', 'name', 'username']);
}
}
For a small to medium table a like query is plenty. If you outgrow it, Laravel Scout adds full text search over the same code with a driver swap, but do not reach for it until you need it.
The search box
Just a normal input and an empty list for the suggestions.
<input type="search" id="search" placeholder="Search" autocomplete="off">
<ul id="suggestions"></ul>
The JavaScript
Two things make this feel good: only fire after the user pauses typing (debounce), so you are not hitting the server on every keystroke, and render the results as you get them.
const input = document.getElementById('search');
const list = document.getElementById('suggestions');
let timer;
input.addEventListener('input', () => {
clearTimeout(timer);
timer = setTimeout(search, 250); // wait 250ms after the last keystroke
});
async function search() {
const term = input.value.trim();
if (term.length < 2) {
list.innerHTML = '';
return;
}
const response = await fetch(`/search?q=${encodeURIComponent(term)}`);
const users = await response.json();
list.innerHTML = users
.map((u) => `<li><a href="/users/${u.username}">${u.name} (@${u.username})</a></li>`)
.join('');
}
That setTimeout and clearTimeout pair is the debounce. Each keystroke cancels the pending search and starts a new 250 millisecond timer, so the request only fires once the user stops typing.
That is the whole thing
A JSON endpoint and around twenty lines of JavaScript. No jQuery, no Typeahead, no Bloodhound, no third party search package. When you need more, like grouping results or full text relevance, you grow into Scout, but this simple version covers most live search needs. Questions welcome in the comments.
All comments ()
No comments yet
Be the first to leave a comment on this post.