article

Wednesday, May 14, 2025

Laravel 12 React Pagination and Search | React Starter kit

Laravel 12 React Pagination and Search | React Starter kit

Download Laravel App

https://laravel.com/docs/12.x/installation

Connecting our Database

open .env file root directory.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=8889
DB_DATABASE=laravel12dev
DB_USERNAME=root
DB_PASSWORD=root

Database Migration
php artisan migrate

myapp>php artisan migrate
Migration table created successfully.
check database table

php artisan make:controller UsersController change it with the following codes:
app\Http\Controllers\UsersController.php

//app\Http\Controllers\UsersController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Inertia\Inertia;
use App\Models\User;

class UsersController extends Controller
{
    public function home()
    {
        $query = User::select('id', 'name', 'email', 'created_at')->latest();

        // Handle search
        if (request()->has('search')) {
            $search = request('search');
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%");
            });
        }

        $users = $query->paginate(10);

        return Inertia::render('users/index', [
            'users' => $users,
            'filters' => [
                'search' => request('search', ''),
            ]
        ]);
    }
}
Frontend with React and InertiaJS
Add sidebar menu product mainNavItems from reactjs\rescourses\js\components\app-sidebar.tsx
File: pages\users\index.tsx
reactjs\resources\js\pages\users\index.tsx
//reactjs\resources\js\pages\users\index.tsx
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, router } from '@inertiajs/react';
import { Button } from '@/components/ui/button';
import { Search, ChevronLeft, ChevronRight } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { useState } from 'react';

interface User {
    id: number;
    name: string;
    email: string;
    created_at: string;
}

interface Props {
    users: {
        data: User[];
        current_page: number;
        last_page: number;
        per_page: number;
        total: number;
        from: number;
        to: number;
    };
    filters: {
        search: string;
        filter: string;
    };
}

const breadcrumbs: BreadcrumbItem[] = [
    {
        title: 'Users',
        href: '/admin/users',
    },
];

export const generatePaginationnumber = (currentPage, totalPages) => {

    if (totalPages <= 7) {
       return Array.from({ length: totalPages }, (_, i) => i + 1); 
    }
      
    if (currentPage <= 3) {
        return [1, 2, 3 , "...", totalPages - 1, totalPages];
    }
     
    if (currentPage >= totalPages - 3) {
       console.log("2");
       return [1, 2, 3, "...", totalPages - 2, totalPages - 1, totalPages];
    }
     
    return [
      1,
      "...",
      currentPage - 1,
      currentPage,
      currentPage + 1,
      "...",
      totalPages,
    ];
};

export default function UsersIndex({ users, filters }: Props) {
    //console.log(users); 

    const params = new URLSearchParams(window.location.search);
    const currentPage = Number(params.get("page")) || 1;
    //console.log(currentPage); 

    const totalPages = Math.ceil(Number(users.total) / Number(users.per_page));
    //const totalPages = users.total;
    console.log(totalPages);

    const allPages = generatePaginationnumber(currentPage, totalPages);

    const [searchTerm, setSearchTerm] = useState(filters.search);

    const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        router.get(route('users.home'), {
            search: searchTerm,
        }, {
            preserveState: true,
            preserveScroll: true,
        });
    };
    
    const handlePageChange = (page: number) => {
        router.get(route('users.home'), {
            page,
        }, {
            preserveState: true,
            preserveScroll: true,
        });
    };

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title="Users" />
            <div className="flex h-full flex-1 flex-col gap-6 rounded-xl p-6 bg-gradient-to-br from-background to-muted/20">
                <div className="flex gap-4 mb-4">
                    <form onSubmit={handleSearch} className="relative flex-1">
                        <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
                        <Input placeholder="Search name or email..."
                            value={searchTerm}
                            onChange={(e) => setSearchTerm(e.target.value)}
                            className="pl-10 bg-gray-200  text-black"
                        />
                    </form>                                    
                </div>
                <div className="rounded-md border">
                    <div className="relative w-full overflow-auto">
                        <table className="w-full caption-bottom text-sm  bg-white rounded-lg">
                            <thead>
                                <tr className="bg-gray-200 text-black">
                                    <th className="py-2 px-4 text-left border-b">ID</th>
                                    <th className="py-2 px-4 text-left border-b">Name</th>
                                    <th className="py-2 px-4 text-left border-b">Email</th>
                                    <th className="py-2 px-4 text-left border-b">Created At</th>                                   
                                    <th className="py-2 px-4 text-right border-b">Actions</th>                                
                                </tr>                            
                            </thead>     
                            <tbody>
                                {users.data.map((user) => (
                                    <tr key={user.id} className="hover:bg-gray-50 text-black">
                                        <td className="p-4 align-middle font-medium border-b">{user.id}</td>
                                        <td className="p-4 align-middle font-medium border-b">{user.name}</td>
                                        <td className="p-4 align-middle font-medium border-b">{user.email}</td>
                                        <td className="p-4 align-middle font-medium border-b">{new Date(user.created_at).toLocaleDateString()}</td>
                                        <td className="p-4 align-middle text-right border-b">
                                            <div className="flex justify-end gap-2">
                                                <Button variant="ghost"
                                                    className="w-full sm:w-auto bg-blue-600 text-white rounded-lg px-4 py-2 hover:bg-blue-700"
                                                >
                                                    Edit
                                                </Button>                                                
                                                <Button variant="ghost"
                                                    className="w-full sm:w-auto bg-red-600 text-white rounded-lg px-4 py-2 hover:bg-red-700 transition transition ml-2"
                                                >
                                                    Delete
                                                </Button>                                            
                                            </div>
                                        </td>
                                    </tr>
                                ))}
                                {users.total === 0 && (
                                    <tr>
                                        <td colSpan={6} className="p-4 text-center text-muted-foreground">
                                            No users Found                                        
                                        </td>
                                    </tr>
                                )}
                            </tbody> 
                        </table>                    
                    </div>                
                </div>

                {/* Pagination */}
                <div className="flex items-center justify-between px-2">
                    <div className="text-sm text-muted-foreground">
                        Showing {users.from} to {users.to} of {users.total} results  
                    </div>                    
                    <div className="flex items-center space-x-2">
                        <Button variant="outline"
                            size="icon"
                            onClick={() => handlePageChange(users.current_page - 1)}
                            disabled={users.current_page === 1}
                        >
                            <ChevronLeft className="h-4 w-4" />
                        </Button>   
                        <div className="flex items-center space-x-1">
                        {allPages.map((page, index) => (

                            <Button key={index}
                            variant={page === users.current_page ? "default" : "outline"}
                            size="icon"
                            onClick={() => handlePageChange(page)}
                            >
                            {page}
                            </Button> 
                           
                        ))}
                        </div>                        
                        <Button variant="outline"
                            size="icon"
                            onClick={() => handlePageChange(users.current_page + 1)}
                            disabled={users.current_page === users.last_page}
                        >
                            <ChevronRight className="h-4 w-4" />
                        </Button>                    
                    </div>                
                </div> 
            </div>
        </AppLayout>
    );
}
Routes
routes/web.php
//routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Controllers\UsersController;

Route::get('/', function () {
    return Inertia::render('welcome');
})->name('home');

Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('dashboard', function () {
        return Inertia::render('dashboard');
    })->name('dashboard');

    Route::get('/admin/users-pagination-search', [UsersController::class, 'home'])->name('users.home');
});

require __DIR__.'/settings.php';
require __DIR__.'/auth.php';
Run php artisan serve and npm run dev myapp>composer run dev
Starting Laravel development server: http://127.0.0.1:8000

Related Post