Pagination in CodeIgniter 4 with Example

โšก Smart Summary

Pagination in CodeIgniter breaks large database result sets into small, navigable pages using the built-in pagination library. This walkthrough builds a working example that queries an authors table, configures per-page limits, and renders numbered navigation links.

  • ๐Ÿ—‚๏ธ Core Purpose: Pagination divides large query results into manageable pages, improving readability and reducing page load time.
  • โš™๏ธ Library Setup: Load the pagination and database libraries, then pass a $config array defining base_url, total_rows, and per_page.
  • ๐Ÿ”ข URI Segment: The uri_segment value tells CodeIgniter which URL segment carries the offset used to skip already shown records.
  • ๐Ÿงฑ Model Layer: A model exposes get_count() and get_authors($limit, $start) so the controller fetches only the rows one page needs.
  • ๐Ÿ”— Navigation Links: The create_links() method generates ready-to-style previous, numbered, and next links automatically.
  • ๐ŸŽจ Custom Styling: Configuration keys such as full_tag_open and cur_tag_open wrap the links in your own CSS classes or framework markup.
  • ๐Ÿค– AI Assist: AI tools can scaffold the model, controller, and $config array from a table definition, then explain each option.

Pagination in CodeIgniter

Databases are living entities. They grow as time passes by. When designing applications, we need to take into consideration the many records that the users will have to browse through. Pagination allows us to break down our results into small manageable parts. The following image shows an example of pagination on Google search.

Pagination in Codeigniter

In this tutorial, you are going to learn how to paginate database results in CodeIgniter using the pagination library. This tutorial presumes that you are familiar with the basics of CodeIgniter Active Record. If you do not know it yet, then you can read the previous tutorials. The other assumption made is that you have already downloaded CodeIgniter.

Database configuration

We will start with creating the database and inserting some dummy records in it. It will be a single table database with 50 records on it.

Run the following script against MySQL to create the database authors table. Insert 50 dummy records.

CREATE SCHEMA ci_pagination;
 
DROP TABLE IF EXISTS `authors`;
 
CREATE TABLE `authors` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`birthdate` date NOT NULL,
`added` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (1, 'Brielle', 'O\'Hara', 'alexandre67@example.com', '1974-05-21', '1999-09-17 19:17:28');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (2, 'Flavio', 'Lehner', 'mafalda85@example.net', '1971-10-14', '1998-09-09 00:25:06');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (3, 'Elmira', 'Johns', 'wellington52@example.org', '1983-11-28', '2015-02-07 15:56:43');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (4, 'Elza', 'Mitchell', 'marisol46@example.org', '1989-03-08', '1992-08-21 00:21:39');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (5, 'Viva', 'Greenfelder', 'era94@example.com', '1995-04-17', '2017-04-30 05:55:39');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (6, 'Maxwell', 'VonRueden', 'mcassin@example.net', '1994-07-01', '1996-05-08 23:30:14');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (7, 'Deontae', 'Becker', 'rory.kub@example.org', '1992-02-19', '2017-07-22 11:49:15');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (8, 'Sylvester', 'Christiansen', 'erohan@example.org', '1990-09-03', '2004-05-08 08:15:37');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (9, 'Torrey', 'Strosin', 'adams.luciano@example.net', '1999-10-09', '2009-08-30 21:30:44');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (10, 'Kelli', 'Smitham', 'daniel.catalina@example.com', '2012-11-07', '1986-01-22 20:52:57');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (11, 'Abigale', 'Schuppe', 'andreanne.hayes@example.net', '2018-02-18', '1994-05-07 06:26:36');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (12, 'Letha', 'Gleason', 'eldridge.heaney@example.net', '2009-02-06', '1998-05-25 04:37:54');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (13, 'Sienna', 'Upton', 'monique57@example.org', '2017-08-17', '2009-08-08 19:08:10');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (14, 'Harley', 'Gutkowski', 'breilly@example.net', '1987-11-30', '1998-07-31 11:08:01');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (15, 'Lois', 'Bednar', 'hpouros@example.net', '2002-02-11', '2001-07-26 15:04:16');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (16, 'Gail', 'McDermott', 'reina.kerluke@example.com', '1987-03-30', '2004-12-15 20:38:29');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (17, 'Sydney', 'Strosin', 'dweber@example.net', '1985-04-29', '2010-08-07 08:50:35');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (18, 'Anne', 'Cremin', 'fritz.schmitt@example.com', '1976-02-15', '1980-03-25 05:29:41');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (19, 'Norberto', 'Bergnaum', 'franecki.javon@example.net', '1971-03-11', '1993-03-20 23:36:25');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (20, 'Arvid', 'Johns', 'pschultz@example.org', '2016-01-03', '1995-04-30 17:07:15');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (21, 'Bernita', 'Fay', 'arne96@example.org', '1983-12-26', '1987-02-23 16:55:28');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (22, 'Gonzalo', 'Gorczany', 'velma.huels@example.com', '1987-10-19', '2016-10-18 19:25:46');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (23, 'Jackie', 'Klein', 'gretchen.howe@example.com', '1971-01-20', '2010-11-16 02:58:05');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (24, 'Andrew', 'Kessler', 'collins.faye@example.org', '1984-08-07', '1971-12-15 08:46:42');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (25, 'Claire', 'Hudson', 'madalyn.wunsch@example.org', '1984-12-19', '1991-07-04 14:35:53');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (26, 'Prudence', 'Runte', 'koch.laurie@example.net', '2008-12-27', '1985-09-30 04:57:55');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (27, 'Destinee', 'Romaguera', 'tromp.tiffany@example.com', '1978-11-09', '1997-11-09 19:58:08');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (28, 'Marianna', 'Harvey', 'clovis.schuppe@example.com', '2013-08-28', '1990-08-06 19:29:19');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (29, 'Eloy', 'Lakin', 'wmorissette@example.net', '1989-12-08', '1974-03-16 03:21:16');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (30, 'Rubie', 'McClure', 'haley.louisa@example.net', '1987-09-12', '1998-07-02 10:45:36');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (31, 'Marisa', 'Stracke', 'zachary76@example.org', '1975-05-28', '1975-03-19 00:57:35');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (32, 'Jacey', 'Beatty', 'dahlia.hermann@example.org', '1979-12-17', '1971-01-21 16:50:58');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (33, 'Idella', 'Ruecker', 'elda.reichert@example.com', '2009-09-15', '1996-04-19 22:27:31');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (34, 'Dimitri', 'Bednar', 'freida.parker@example.org', '1998-12-02', '2008-12-30 23:29:57');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (35, 'Elfrieda', 'Smitham', 'padberg.rex@example.org', '2018-07-23', '1972-04-01 07:52:25');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (36, 'Dwight', 'Walter', 'gmosciski@example.org', '2016-08-15', '1994-07-02 11:06:55');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (37, 'Macie', 'Fay', 'uschuppe@example.net', '1976-01-29', '2005-12-13 18:44:46');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (38, 'Lori', 'Kshlerin', 'mhansen@example.net', '1983-04-08', '1986-01-18 16:03:52');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (39, 'Jeffry', 'Paucek', 'alisha35@example.net', '2002-10-02', '2004-03-29 07:06:03');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (40, 'Yvonne', 'Bernhard', 'jaqueline21@example.net', '2017-11-04', '1986-12-15 23:55:23');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (41, 'Ludwig', 'Heathcote', 'marcelino.kirlin@example.org', '1981-02-09', '2000-08-02 20:45:48');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (42, 'Jadyn', 'Wiegand', 'georgianna.swift@example.net', '1982-06-16', '1980-12-05 13:09:37');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (43, 'Ferne', 'Smitham', 'melany22@example.com', '1996-12-17', '1984-11-18 19:26:27');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (44, 'Meta', 'Corkery', 'xharber@example.com', '1972-03-31', '2007-01-20 00:07:31');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (45, 'Toni', 'Wolf', 'wyman.crystal@example.org', '2012-04-15', '1973-06-22 12:14:37');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (46, 'Zack', 'Luettgen', 'deion.konopelski@example.org', '2003-05-29', '2018-11-19 05:03:21');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (47, 'Kameron', 'Dietrich', 'ronaldo.torphy@example.com', '1973-05-20', '1974-09-16 20:27:17');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (48, 'Zetta', 'Lebsack', 'timmothy.vandervort@example.com', '1996-11-04', '2001-03-06 01:33:01');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (49, 'Benny', 'Hane', 'leone.lueilwitz@example.net', '2008-02-02', '2006-01-27 22:12:39');
INSERT INTO `authors` (`id`, `first_name`, `last_name`, `email`, `birthdate`, `added`) VALUES (50, 'Evans', 'Gleason', 'ccummerata@example.org', '1999-09-09', '1990-09-26 17:50:11');

Now that we have created our database successfully and inserted dummy records in it, let us configure our CodeIgniter application to communicate with the database.

Open application/config/database.php

Set the database connection parameters similar to the following

$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => 'root',
'password' => 'melody',
'database' => 'ci_pagination',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);

HERE,

  • ‘hostname’ => ‘localhost’, sets the host name of the database
  • ‘username’ => ‘root’, specifies the database user name
  • ‘password’ => ‘melody’, sets the database password
  • ‘database’ => ‘ci_pagination’, specifies the database name.

We will also be autoloading the database library when our application starts. Let us do that. Open application/config/autoload.php

Load the database library as shown by the code below

$autoload['libraries'] = array('database');

That’s it for the database configuration.

Let us now work on the database model for pagination.

CodeIgniter Pagination Database Model

Create a new model Authors_model in application/models

Add the following code

<?php
 
class Authors_model extends CI_Model {
 
protected $table = 'authors';
 
public function __construct() {
parent::__construct();
}
 
public function get_count() {
return $this->db->count_all($this->table);
}
 
public function get_authors($limit, $start) {
$this->db->limit($limit, $start);
$query = $this->db->get($this->table);
 
return $query->result();
}
}

HERE,

  • protected $table = ‘authors’; defines a protected variable that holds the database table name for the model
  • public function __construct() {…} calls the parent constructor method
  • public function get_count() {…} returns the total records in the database table. This is needed for pagination purposes
  • public function get_authors($limit, $start) {…} defines a method that will be used to retrieve paginated results from the database. We pass in the limit and the start point. The limit defines the total number of records to be returned while start defines the number of records that should be skipped

That’s it for our database model. Let us now create the routes that will be responding to our paginated results.

CodeIgniter Pagination Routes

Open the routes file in application/config/routes.php

Add the following route

$route['authors/(:num)'] = 'authors';

HERE,

  • We define a route authors that accepts an optional parameter of the number. The optional parameter is defined by using the opening and closing brackets. The defined route calls the index method in the Authors controller.

Let us now move on to the controller for our paginated results.

CodeIgniter Pagination Controller

Create a new file Authors.php in application/controllers directory

Add the following code

<?php defined('BASEPATH') OR exit('No direct script access allowed');
 
class Authors extends CI_Controller {
 
public function __construct() {
parent:: __construct();
 
$this->load->helper('url');
$this->load->model('authors_model');
$this->load->library("pagination");
}
 
public function index() {
$config = array();
$config["base_url"] = base_url() . "authors";
$config["total_rows"] = $this->authors_model->get_count();
$config["per_page"] = 10;
$config["uri_segment"] = 2;
 
$this->pagination->initialize($config);
 
$page = ($this->uri->segment(2)) ? $this->uri->segment(2) : 0;
 
$data["links"] = $this->pagination->create_links();
 
$data['authors'] = $this->authors_model->get_authors($config["per_page"], $page);
 
$this->load->view('authors/index', $data);
}
}

HERE,

  • class Authors extends CI_Controller {…} we define a class Authors that extends the CI_Controller class
  • public function __construct() {…} this method initializes the parent constructor and loads the url helper, authors model and the pagination library.
  • public function index() {…} defines the method that responds to our route authors
    • $config[“base_url”] = base_url() . “authors”; sets the pagination URL that will be used to generate pagination links
    • $config[“total_rows”] = $this->authors_model->get_count(); sets the total rows that need to be paginated. The value is retrieved from the authors model by calling the get_count method.
    • $config[“per_page”] = 10; defines the rows that need to be displayed per page
    • $config[“uri_segment”] = 2; specifies the URL segment that contains the value that will be used to skip records
    • $this->pagination->initialize($config); initializes the pagination library using the config array values that we defined
    • $page = ($this->uri->segment(2)) ? $this->uri->segment(2) : 0; checks if the skip number has been set in the second segment of the URI and if it is not set then the value 0 is assigned to the variable $page
    • $data[“links”] = $this->pagination->create_links(); creates the pagination links and assigns them to the links key of the $data array variable.
    • $data[‘authors’] = $this->authors_model->get_authors($config[“per_page”], $page); retrieves the paginated authors records and assigns them to the authors key of the $data array variable
    • $this->load->view(‘authors/index’, $data); loads the index view in the authors directory and passes in the array variable $data.

That’s it for our controller. Let us now create the view that will display our database results.

Create a new directory authors in application/views

Create a new file index.php in application/views/authors/index.php

Add the following code

<!DOCTYPE html>
<html>
<head>
<title>CodeIgniter Pagination</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https:0
</head>
<body>
<div class="container">
<h3 class="title is-3">CodeIgniter Database Pagination</h3>
<div class="column">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>Contact Name</th>
<th>Contact Number</th>
<th>Email</th>
<th>City</th>
</tr>
</thead>
<tbody>
<?php foreach ($authors as $author): ?>
<tr>
<td><?= $author->id ?></td>
<td><?= $author->first_name ?></td>
<td><?= $author->last_name ?></td>
<td><?= $author->email ?></td>
<td><?= $author->birthdate ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p><?php echo $links; ?></p>
</div>
</div>
</body>
</html>

HERE,

  • <?php foreach ($authors as $author): ?> loops through the results of the $authors variable and prints the results in a table
  • <p><?php echo $links; ?></p> prints the pagination links at the bottom of our table.

Let us now start our application and see the results.

In this tutorial, we are using the built-in PHP web server, but you can use any web server that supports PHP.

Open the terminal

Run the following command

cd C:\Sites\ci-app
php -S localhost:3000

HERE,

  • The above command browses to the application code directory and starts the built-in server on port 3000.

Note: the application path has to match the path where you downloaded CodeIgniter. You can use any port number that is free on your computer. It is not necessary to use port 3000.

Open the web browser and load the following URL

http://localhost:3000/authors

You should be able to get results similar to the following

CodeIgniter Pagination Controller

How to Customize CodeIgniter Pagination Links

By default, the create_links() method outputs plain anchor tags. You can wrap these links in your own markup so they match a CSS framework such as Bootstrap or Bulma. Add the styling keys to the same $config array before you call initialize().

$config['full_tag_open'] = '<nav><ul class="pagination">';
$config['full_tag_close'] = '</ul></nav>';
$config['num_tag_open'] = '<li class="page-item">';
$config['num_tag_close'] = '</li>';
$config['cur_tag_open'] = '<li class="page-item active"><a class="page-link">';
$config['cur_tag_close'] = '</a></li>';
$config['next_link'] = 'Next &raquo;';
$config['prev_link'] = '&laquo; Previous';

The most useful configuration keys are described below.

  • full_tag_open / full_tag_close: markup that wraps the entire set of links, ideal for a <nav> or <ul> container.
  • num_tag_open / num_tag_close: markup around each numbered page link.
  • cur_tag_open / cur_tag_close: markup around the current, active page number.
  • next_link / prev_link: the text or symbols used for the next and previous buttons.

Because the styling lives entirely in configuration, you can restyle pagination across the whole application by editing a single array, without touching your controller logic.

FAQs

The per_page value sets how many records appear on one page, while uri_segment tells CodeIgniter which URL segment holds the offset number. Together they control how many rows are shown and which page loads.

Usually the uri_segment value does not match the position of the offset in your URL. Count the segments after your domain and set uri_segment to that number so create_links() reads the correct page offset.

Yes. Pagination only needs a total_rows count and a per_page value. You can paginate arrays, API results, or files by supplying the total manually and slicing the data for each page yourself.

Yes. Given a table name and columns, AI can produce the model, the $config array, and the controller index() method. Review the base_url and uri_segment values, then test the generated links.

Yes. Paste your controller and URL structure, and AI can spot mismatched uri_segment values, missing base_url slashes, or an incorrect total_rows source, then suggest the corrected configuration to try.

Summarize this post with: