Pagination in PHP with MySQL
In this lesson: Show MySQL records page by page in PHP — LIMIT, OFFSET, page links and PDO — complete working code.
Why pagination
When a table has hundreds of rows, showing them all on one page is slow and hard to read. Pagination splits the results into pages — say 10 per page — with links to move between them. It keeps pages fast and gives users a clean way to browse.
LIMIT (how many) and OFFSET (how many to skip).The idea: LIMIT and OFFSET
LIMIT says how many rows to return; OFFSET says how many to skip first.
-- page 1: first 10 rows
SELECT * FROM products LIMIT 10 OFFSET 0;
-- page 2: skip 10, take next 10
SELECT * FROM products LIMIT 10 OFFSET 10;
Each page changes only the OFFSET. Page 3 would skip 20, page 4 would skip 30, and so on.
Calculating the offset
The offset comes from the page number with a simple formula.
<?php
$perPage = 10;
$page = max(1, (int)($_GET["page"] ?? 1));
$offset = ($page - 1) * $perPage;
// page 3 -> offset = (3-1)*10 = 20
?>
We cast page to an integer for safety and use max(1, ...) so the page is never below 1. The formula (page - 1) * perPage gives the rows to skip.
The paginated query
<?php
// total pages
$total = $pdo->query("SELECT COUNT(*) FROM products")->fetchColumn();
$totalPages = ceil($total / $perPage);
// rows for this page
$stmt = $pdo->prepare("SELECT * FROM products LIMIT ? OFFSET ?");
$stmt->bindValue(1, $perPage, PDO::PARAM_INT);
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll();
?>
First we count all rows and divide by per-page (rounded up with ceil) to get the total pages. Then we fetch just this page's rows using LIMIT and OFFSET bound as integers.
Building page links
<?php
for ($i = 1; $i <= $totalPages; $i++) {
echo "<a href='?page=$i'>$i</a> ";
}
?>
A simple loop prints a link for each page. Clicking a number sets ?page=N in the URL, which your offset calculation reads to show that page.
Common mistakes
- Forgetting to cast the page number. Use
(int)so LIMIT/OFFSET stay safe. - Allowing page 0 or negative. Use
max(1, ...)to keep the page valid. - Not rounding total pages up. Use
ceil(), or the last few rows get no page. - Binding LIMIT as a string. Bind with
PDO::PARAM_INTso MySQL accepts it.
Summary
- Pagination splits results into pages for speed and readability.
LIMITsets how many rows;OFFSETsets how many to skip.- Offset formula:
(page - 1) * perPage. - Total pages:
ceil(totalRows / perPage). - Loop to print numbered page links using
?page=N.
Next, run the queries safely with prepared statements, or read results with PHP CRUD.
Frequently Asked Questions
What is pagination in PHP?
How do LIMIT and OFFSET work?
LIMIT sets how many rows to return, and OFFSET sets how many to skip first. For page 2 with 10 per page, you use LIMIT 10 OFFSET 10 — skip the first 10, then take the next 10.How do I calculate the offset for a page?
offset = (page - 1) * perPage. For page 3 with 10 per page, that is (3-1)*10 = 20, so you skip 20 rows and show rows 21 to 30.How do I find the total number of pages?
SELECT COUNT(*), then divide by the per-page number and round up with ceil(). For 95 rows at 10 per page, that is ceil(95/10) = 10 pages.Should I use LIMIT with user input directly?
(int)$_GET["page"]). Because LIMIT/OFFSET take numbers, casting to int keeps them safe. Always validate the page is at least 1.