Laravel Pagination with ReactJS using Vite
In today's tutorial, we will see how to implement Laravel React js pagination using vite, as you know pagination is dividing content into several pages with links based on the current page.
Create the migration
I assume that you have already created a new Laravel application with React js already installed, if you do not know how to add Reactjs to Laravel 9 follow this tutorial.
Let's create the database with a tasks table, so the first thing we will do is create the migration.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->boolean('done')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tasks');
}
};
Create the controller
Next, let's create the TaskController with the method index which returns all the tasks paginated.
<?php
namespace App\Http\Controllers;
use App\Models\Task;
use Illuminate\Http\Request;
class TaskController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
return Task::paginate(10);
}
}
Create the route
Next, we will add the route in the file api.php.
Route::apiResource('tasks', TaskController::class);
Create the home component
In resources/js, we add new folder components inside the created folder we add a new file Home.jsx in which we implement the pagination with data retrieved from the API.
The structure of the folders:
- resources
- js
- components
- Home.jsx
- components
- js
import axios from 'axios';
import React, { useEffect, useState } from 'react'
export default function Home() {
const [tasks, setTasks] = useState([]);
const [page, setPage] = useState(1);
useEffect(() => {
fetchTasks();
}, [page])
const fetchTasks = async () => {
try {
const response = await axios.get(`/api/tasks?page=${page}`);
setTasks(response.data);
} catch (error) {
console.log(error);
}
}
const checkIfTaskIsDone = (done) => (
done ?
(
<span className='badge bg-success'>
Done
</span>
)
:
(
<span className='badge bg-danger'>
Processing...
</span>
)
)
const fetchNextPrevTasks = (link) => {
const url = new URL(link);
setPage(url.searchParams.get('page'));
}
const renderPaginationLinks = () => {
return <ul className="pagination">
{
tasks.links?.map((link,index) => (
<li key={index} className="page-item">
<a style={{cursor: 'pointer'}} className={`page-link ${link.active ? 'active' : ''}`}
onClick={() => fetchNextPrevTasks(link.url)}>
{link.label.replace('«', '').replace('»', '')}
</a>
</li>
))
}
</ul>
}
return (
<div className="my-5">
<div className="card">
<div className="card-body">
<table className="table">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Body</th>
<th>Done</th>
<th>Created</th>
</tr>
</thead>
<tbody>
{
tasks.data?.map(task => (
<tr key={task.id}>
<td>{task.id}</td>
<td>{task.title}</td>
<td>{task.body}</td>
<td>
{
checkIfTaskIsDone(task.done)
}
</td>
<td>{task.created_at}</td>
</tr>
))
}
</tbody>
</table>
<div className="my-4 d-flex justify-content-between">
<div>
Showing {tasks.from} to {tasks.to} from {tasks.total} results.
</div>
<div>
{renderPaginationLinks()}
</div>
</div>
</div>
</div>
</div>
)
}
Update app.js & welcome page
Next, rename the file app.js to app.jsx, let's import the home component and add it to the app.
and do not forget to add the id app to your welcome page.
//app.jsx
import './bootstrap';
import ReactDOM from 'react-dom/client';
import Home from './components/Home';
ReactDOM.createRoot(document.getElementById('app')).render(
<Home />
);
//welcome page
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<title>Laravel React CRUD</title>
</head>
<body class="bg-light">
<div class="container" id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
@viteReactRefresh
@vite(['resources/js/app.jsx'])
</body>
</html>