How to Build a Real-Time Live Search in Laravel 12 with AJAX and jQuery

How to Build a Real-Time Live Search in Laravel 12 with AJAX and jQuery

Live search, also known as instant search or autocomplete, allows users to see relevant results as they type into a search field. This reduces wait times and improves usability, leading to better engagement and lower bounce rates.

AJAX (Asynchronous JavaScript and XML) is the technology that enables this by allowing data to be sent to and retrieved from the server asynchronously, without reloading the page. In Laravel, AJAX pairs perfectly with routes and controllers to handle requests efficiently.

  • Improved UX: Users get immediate results without page reloads.
  • Efficiency: Only the necessary data is fetched from the server, reducing bandwidth and server load.
  • SEO Boost: Faster, interactive pages can improve dwell time and overall engagement metrics.

In Laravel 12, enhancements like better routing, improved middleware, and optimized Eloquent queries make AJAX implementations smoother than ever. Laravel 12 requires PHP 8.2+ and offers improved error handling, which we will leverage throughout this tutorial.

By the end of this guide, you will have a fully working live search that queries a products table and displays results instantly as the user types.



Implementing Live Search in Laravel 12 Using AJAX: A Comprehensive Guide

Table Of Content

1 Prerequisites

Before starting, make sure you have:
  • PHP ≥ 8.2
  • Composer
  • MySQL / PostgreSQL / SQLite (or any supported DB)
  • Basic knowledge of Laravel routing, controllers, and Blade

2 Introduction

Live search is a must-have feature for modern web applications. In this guide, you will learn how to implement Laravel 12 live search using AJAX to fetch results in real time without page reloads. We will build this feature step-by-step with clean code, performance optimization, and clear explanations.

We will use Eloquent ORM for database queries, jQuery for AJAX calls, and Bootstrap for a clean UI. If you are new to Eloquent, check out our Laravel 12 Eloquent Tips and Tricks for Beginners guide first.

3 Create a Fresh Laravel 12 Project

3.1 Install Laravel Project

First, ensure Composer is installed on your system. Use the following command to install a new Laravel Project:

composer create-project laravel/laravel:^12.0 live-search

Navigate to your project directory:

cd live-search

3.2 Configure Your Database (.env)

Open the .env file and input the necessary database credentials:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=live_search_db
DB_USERNAME=root
DB_PASSWORD=

This setup ensures Laravel connects seamlessly to MySQL. In Laravel 12, database configurations are more flexible, supporting multiple connections out of the box.

4 Creating Migrations and Seeding Data

Migrations in Laravel allow version-controlled database schema management.

Generate Migration: Run

    
        php artisan make:migration create_products_table
    
Edit Migration File database/migrations:
    
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('description');
            $table->decimal('price', 10, 2);
            $table->timestamps();
        });
    }

    public function down(): void {
        Schema::dropIfExists('products');
    }
};

Run the migration:

php artisan migrate

Create the Product Model:

The controller references App\Models\Product, so we need to generate the Eloquent model:

    
        php artisan make:model Product
    

This creates app/Models/Product.php. Laravel will automatically map it to the products table. No additional configuration is needed for this tutorial.

Create Seeder: Run

    
        php artisan make:seeder ProductSeeder
    
Edit Seeder File database/seeders/ProductSeeder.php:
    
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class ProductSeeder extends Seeder {
    public function run(): void {
        DB::table('products')->insert([
            ['title' => 'T-shirt', 'description' => 'A comfortable cotton t-shirt.', 'price' => 15.00],
            ['title' => 'Jeans', 'description' => 'Classic blue denim jeans.', 'price' => 45.50],
            ['title' => 'Laptop', 'description' => 'Powerful laptop for all your needs.', 'price' => 1200.00],
            ['title' => 'Keyboard', 'description' => 'Mechanical keyboard with RGB lighting.', 'price' => 75.25],
            ['title' => 'Mouse', 'description' => 'Ergonomic wireless mouse.', 'price' => 30.00],
        ]);
    }
}

Update DatabaseSeeder.php: Add $this->call(ProductSeeder::class); inside the run() method.

Run Seeder:
    
        php artisan db:seed
    

This populates your table with sample data. In real applications, use factories for larger datasets. Laravel 12's seeding is optimized for performance.

5 Building the Controller for Search Logic

Create a controller to handle the data loading functionality:

php artisan make:controller SearchController

Edit app/Http/Controllers/SearchController.php
    
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product;

class SearchController extends Controller {
    public function index() {
        return view('search');
    }

    public function search(Request $request) {
        $request->validate(['search' => 'nullable|string|max:255']);

        if ($request->ajax()) {
            $products = Product::where('title', 'like', '%' . $request->search . '%')->get();

            $output = '';
            if ($products->count() > 0) {
                foreach ($products as $product) {
                    $output .= '<tr>' .
                        '<td>' . e($product->id) . '</td>' .
                        '<td>' . e($product->title) . '</td>' .
                        '<td>' . e($product->description) . '</td>' .
                        '<td>' . e($product->price) . '</td>' .
                        '</tr>';
                }
            } else {
                $output .= '<tr><td colspan="4">No results found</td></tr>';
            }

            return response($output);
        }
    }
}

Explanation:

  • The index method loads the Blade view.
  • The search method checks for an AJAX request, then queries the products table using Eloquent's where...like for fuzzy matching.
  • We wrap output values with Laravel's e() helper to escape HTML entities and prevent XSS attacks.
  • Input validation with nullable|string|max:255 prevents invalid or oversized queries.
  • The closing <?php ... ?> tag is omitted following PSR-12 coding standards.

For a deeper understanding of how Laravel handles requests and middleware, see our Laravel 12 Routing and Middleware Complete Guide.

6 Creating the Front-End View with jQuery AJAX

The view handles user input and displays results.

Create resources/views/search.blade.php:
    
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="_token" content="{{ csrf_token() }}">
    <title>Live Search in Laravel 12</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
    <div class="container mt-5">
        <h3>Products Info</h3>
        <input type="text" class="form-control" id="search" placeholder="Search products...">
        <p id="search-status" class="text-muted mt-1" style="display:none;">Searching...</p>
        <table class="table table-bordered table-hover mt-3">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Product Name</th>
                    <th>Description</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody></tbody>
        </table>
    </div>
    <script>
        $.ajaxSetup({
            headers: {
                'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content')
            }
        });

        let debounceTimer;
        $('#search').on('keyup', function(){
            let query = $(this).val();
            clearTimeout(debounceTimer);
            $('#search-status').show();

            debounceTimer = setTimeout(function(){
                $.ajax({
                    type: 'GET',
                    url: '{{ url("search") }}',
                    data: { search: query },
                    success: function(data){
                        $('tbody').html(data);
                        $('#search-status').hide();
                    },
                    error: function(){
                        $('tbody').html('<tr><td colspan="4">Something went wrong. Please try again.</td></tr>');
                        $('#search-status').hide();
                    }
                });
            }, 300);
        });
    </script>
</body>
</html>
    

Key improvements in this view:

  • Bootstrap 5.3 instead of the outdated Bootstrap 3.3.6 and jQuery 3.7.1 (latest stable).
  • Debounce (300ms): Instead of firing an AJAX request on every keystroke, we wait 300ms after the user stops typing. This drastically reduces server requests.
  • Loading indicator: A "Searching..." message appears while the request is in progress.
  • Error handling: The AJAX error callback shows a user-friendly message if the request fails.
  • CSRF token is included via a meta tag for Laravel's built-in CSRF protection.

7 Define Routes

In routes/web.php, define the necessary routes:

use App\Http\Controllers\SearchController;

Route::get('/', [SearchController::class, 'index']);
Route::get('/search', [SearchController::class, 'search']);

The first route maps the homepage to the index method (loads the Blade view). The second route maps /search to the search method which handles the AJAX query and returns results.

To learn more about route groups, named routes, and middleware layers in Laravel 12, visit our Laravel 12 Routing and Middleware Complete Guide.

8 Folder Structure

9 Run & Test the Application


php artisan serve

Visit: http://127.0.0.1:8000/
Type "Lap" in the search box — you should see "Laptop" appear instantly in the table.

Troubleshooting tips:

  • Check the browser console (F12) for JavaScript errors.
  • Verify your database connection in .env and ensure the products table has been seeded.
  • Ensure the CSRF meta tag is present in the HTML head.
  • If you see a 404 on /search, run php artisan route:list to verify your routes are registered.
  • If you see a 500 error, check storage/logs/laravel.log for details.

For a list of the most common Laravel 12 errors and how to fix them, see our Common Laravel 12 Errors and How to Fix Them guide.

10 Conclusion

You have now implemented a fully working live search in Laravel 12 using AJAX, covering project setup, database migrations, seeding, Eloquent queries, a clean controller, and a responsive front-end with debounce and error handling.

This approach significantly improves user experience by providing instant feedback without page reloads. With clean routing, efficient Eloquent queries, and proper front-end handling, you can build fast, scalable real-time search features suitable for any Laravel application.

Next steps to explore:

  • Add pagination to handle large result sets.
  • Implement search across multiple columns (title + description).
  • Use Laravel's built-in rate limiting to protect the search endpoint.
  • Consider caching frequently searched queries with Redis for better performance.

Continue learning:

If you found this tutorial helpful, check out these related Laravel 12 guides on our site:

Revathi M - PHP and CodeIgniter Developer

Written by Revathi M

PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel

Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.

Frequently Asked Questions

Live search (also called instant search or autocomplete) in Laravel is a feature that displays matching database results in real time as the user types into a search field. It uses AJAX to send asynchronous requests to a Laravel controller, which queries the database with Eloquent and returns results without reloading the page.

Yes, this tutorial is built specifically for Laravel 12 and leverages its latest features including improved Eloquent performance, PHP 8.2+ support, and enhanced error handling.

You need PHP 8.2 or higher, Composer, a database server (MySQL, PostgreSQL, or SQLite), and basic familiarity with Laravel routing, controllers, and Blade templates.

When no matching products are found, the controller returns an HTML table row displaying "No results found" spanning all columns. This is handled by checking the Eloquent query result count in the SearchController.

You can improve performance by adding a debounce timer (e.g., 300ms) on the front end to reduce the number of AJAX requests, indexing the searched database columns, caching frequently searched terms with Redis, and using pagination to limit the number of results returned per query.