Showing 5000 products on one page is slow and unusable. Pagination splits results into pages using SQL LIMIT and OFFSET.
products.php:
PHP
<?php
require 'config/db.php';
$perPage = 8;
$page = max(1, (int)($_GET['page'] ?? 1));
$q = trim($_GET['q'] ?? '');
$like = '%' . $q . '%';
// 1. How many rows in total? (needed to know the number of pages)
$stmt = $pdo->prepare('SELECT COUNT(*) FROM products WHERE name LIKE ?');
$stmt->execute([$like]);
$totalRows = (int)$stmt->fetchColumn();
$totalPages = max(1, (int)ceil($totalRows / $perPage));
$page = min($page, $totalPages);
$offset = ($page - 1) * $perPage;
// 2. Fetch only the rows for this page. LIMIT values are integers we computed, not user text.
$stmt = $pdo->prepare('SELECT * FROM products WHERE name LIKE ? ORDER BY id DESC LIMIT ' . $perPage . ' OFFSET ' . $offset);
$stmt->execute([$like]);
$products = $stmt->fetchAll();
?>
<!-- 3. Display products ... then the pager -->
<nav>
<ul class='pagination'>
<?php if ($page > 1): ?>
<li class='page-item'><a class='page-link' href='?page=<?= $page - 1 ?>&q=<?= urlencode($q) ?>'>Previous</a></li>
<?php endif; ?>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class='page-item <?= $i === $page ? 'active' : '' ?>'>
<a class='page-link' href='?page=<?= $i ?>&q=<?= urlencode($q) ?>'><?= $i ?></a>
</li>
<?php endfor; ?>
<?php if ($page < $totalPages): ?>
<li class='page-item'><a class='page-link' href='?page=<?= $page + 1 ?>&q=<?= urlencode($q) ?>'>Next</a></li>
<?php endif; ?>
</ul>
</nav>Points to note: validate page as an integer; keep search and filter values in every page link; for very many pages show a window of numbers (current page plus or minus 2) with First and Last links; large tables perform better with keyset pagination (WHERE id < :lastId ORDER BY id DESC LIMIT 8), and infinite scroll or a Load more button uses the same query via AJAX.