Skip to content

jQuery Pagination — Complete Guide to Building Page Navigation

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about jquery pagination. We cover key concepts, practical examples, and best practices to help you master this topic.

jQuery pagination splits large data sets into manageable pages, providing Previous/Next buttons, page number links, and configurable page sizes for better user experience and performance.

What You'll Learn

  • Implementing client-side pagination logic
  • Rendering page navigation controls
  • Handling page changes and data updates
  • Building reusable pagination plugins
  • Pagination with AJAX and server-side data

Why It Matters

Displaying 1000 items on one page overwhelms users and hurts performance. Pagination improves load time, reduces DOM size, and helps users navigate content in digestible chunks.

Real-World Use

A search results page that shows 20 results per page with Previous/Next buttons, numbered pages with ellipsis for large ranges, and a "Results X-Y of Z" summary.

Pagination Flow

flowchart TD
    A[Data Array] --> B[Set Page Size]
    B --> C[Calculate Total Pages]
    C --> D[Slice Data for Current Page]
    D --> E[Render Current Page Items]
    E --> F[Render Pagination Controls]
    F --> G[User Clicks Page N]
    G --> D
    style D fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px

Basic Pagination Logic

function Pagination(data, pageSize) {
  this.data = data;
  this.pageSize = pageSize || 10;
  this.currentPage = 1;
  this.totalPages = Math.ceil(data.length / this.pageSize);

  this.getCurrentPageData = function() {
    var start = (this.currentPage - 1) * this.pageSize;
    var end = start + this.pageSize;
    return this.data.slice(start, end);
  };

  this.goToPage = function(page) {
    if (page < 1 || page > this.totalPages) return;
    this.currentPage = page;
    return this.getCurrentPageData();
  };

  this.nextPage = function() {
    return this.goToPage(this.currentPage + 1);
  };

  this.prevPage = function() {
    return this.goToPage(this.currentPage - 1);
  };
}

// Usage
var data = [];
for (var i = 1; i <= 100; i++) {
  data.push({ id: i, name: 'Item ' + i });
}

var pagination = new Pagination(data, 10);
console.log('Page 1:', pagination.getCurrentPageData());
console.log('Page 2:', pagination.goToPage(2));

Expected output: The pagination object manages page state. getCurrentPageData() returns a slice of the data array for the current page.

Rendering Pagination Controls

function renderPagination(pagination, $container) {
  $container.empty();

  // Previous button
  var $prev = $('<button>')
    .text('Previous')
    .prop('disabled', pagination.currentPage === 1)
    .on('click', function() {
      pagination.prevPage();
      updateUI(pagination);
    });
  $container.append($prev);

  // Page numbers
  for (var i = 1; i <= pagination.totalPages; i++) {
    var $pageBtn = $('<button>')
      .text(i)
      .addClass(i === pagination.currentPage ? 'active' : '')
      .on('click', function() {
        var page = parseInt($(this).text());
        pagination.goToPage(page);
        updateUI(pagination);
      });
    $container.append($pageBtn);
  }

  // Next button
  var $next = $('<button>')
    .text('Next')
    .prop('disabled', pagination.currentPage === pagination.totalPages)
    .on('click', function() {
      pagination.nextPage();
      updateUI(pagination);
    });
  $container.append($next);
}

function updateUI(pagination) {
  var pageData = pagination.getCurrentPageData();

  // Render items
  var $list = $('#item-list').empty();
  pageData.forEach(function(item) {
    $list.append('<li>' + item.name + '</li>');
  });

  // Re-render pagination (to update active state)
  renderPagination(pagination, $('#pagination-controls'));
}

// Initialize
var pagination = new Pagination(data, 10);
renderPagination(pagination, $('#pagination-controls'));
updateUI(pagination);

Pagination with Ellipsis

function renderPageNumbers(pagination, $container) {
  var total = pagination.totalPages;
  var current = pagination.currentPage;
  var range = 2; // Pages to show around current

  var pages = [];

  // Always show first page
  pages.push(1);

  // Calculate range
  var start = Math.max(2, current - range);
  var end = Math.min(total - 1, current + range);

  // Ellipsis before range
  if (start > 2) pages.push('...');

  // Middle pages
  for (var i = start; i <= end; i++) {
    pages.push(i);
  }

  // Ellipsis after range
  if (end < total - 1) pages.push('...');

  // Always show last page
  if (total > 1) pages.push(total);

  // Render
  pages.forEach(function(page) {
    if (page === '...') {
      $container.append('<span class="ellipsis">...</span>');
    } else {
      var $btn = $('<button>')
        .text(page)
        .addClass(page === current ? 'active' : '')
        .on('click', function() {
          pagination.goToPage(page);
          updateUI(pagination);
        });
      $container.append($btn);
    }
  });
}

Expected output: For a 50-page set on page 25, the page numbers show: 1 ... 23 24 [25] 26 27 ... 50.

AJAX Pagination (Server-Side)

function ServerPagination($container, options) {
  this.$container = $container;
  this.pageSize = options.pageSize || 20;
  this.currentPage = 1;
  this.totalPages = 1;
  this.apiUrl = options.apiUrl;
  this.loading = false;

  this.loadPage = function(page) {
    if (this.loading) return;
    this.loading = true;
    this.currentPage = page;
    this.$container.addClass('loading');

    $.ajax({
      url: this.apiUrl,
      data: { page: page, pageSize: this.pageSize },
      method: 'GET'
    }).done($.proxy(function(response) {
      this.totalPages = response.totalPages;
      this.renderItems(response.items);
      this.renderControls();
    }, this)).always($.proxy(function() {
      this.loading = false;
      this.$container.removeClass('loading');
    }, this));
  };

  this.renderItems = function(items) {
    // Render item template
  };

  this.renderControls = function() {
    // Render pagination buttons
  };

  this.loadPage(1);
}

Configurable Pagination Plugin

$.fn.pagination = function(options) {
  var settings = $.extend({
    pageSize: 10,
    data: [],
    onPageChange: null
  }, options);

  return this.each(function() {
    var $container = $(this);
    var currentPage = 1;
    var totalPages = Math.ceil(settings.data.length / settings.pageSize);

    function getPageData(page) {
      var start = (page - 1) * settings.pageSize;
      var end = start + settings.pageSize;
      return settings.data.slice(start, end);
    }

    function render() {
      $container.empty();

      var pageData = getPageData(currentPage);

      // Callback
      if ($.isFunction(settings.onPageChange)) {
        settings.onPageChange(pageData, currentPage, totalPages);
      }

      // Navigation
      var $nav = $('<div class="pagination-nav">');

      var $prev = $('<button class="prev">Previous</button>')
        .prop('disabled', currentPage === 1)
        .on('click', function() {
          if (currentPage > 1) {
            currentPage--;
            render();
          }
        });
      $nav.append($prev);

      for (var i = 1; i <= totalPages; i++) {
        (function(page) {
          $nav.append(
            $('<button>').text(page)
              .addClass(page === currentPage ? 'active' : '')
              .on('click', function() {
                currentPage = page;
                render();
              })
          );
        })(i);
      }

      var $next = $('<button class="next">Next</button>')
        .prop('disabled', currentPage === totalPages)
        .on('click', function() {
          if (currentPage < totalPages) {
            currentPage++;
            render();
          }
        });
      $nav.append($next);

      $container.append($nav);
    }

    render();
  });
};

// Usage
$('#pagination-container').pagination({
  pageSize: 5,
  data: myData,
  onPageChange: function(pageData, page, totalPages) {
    $('#page-info').text('Page ' + page + ' of ' + totalPages);
    $('#items').empty();
    pageData.forEach(function(item) {
      $('#items').append('<li>' + item.name + '</li>');
    });
  }
});

Items-Per-Page Selector

$('#page-size-select').on('change', function() {
  var newSize = parseInt($(this).val());
  pagination.pageSize = newSize;
  pagination.totalPages = Math.ceil(pagination.data.length / newSize);
  pagination.currentPage = 1;
  updateUI(pagination);
});

// HTML:
// <select id="page-size-select">
//   <option value="10">10 per page</option>
//   <option value="20">20 per page</option>
//   <option value="50">50 per page</option>
// </select>

Common Mistakes

  1. Not resetting to page 1 when data changes - When filtering or sorting the data, reset currentPage to 1. Otherwise, the user may be on a page that no longer exists.

  2. Forgetting to recalculate totalPages - After data changes, recalculate totalPages = Math.ceil(data.length / pageSize) before checking bounds.

  3. Rendering all page buttons for large datasets - 1000 pages with 1000 buttons is unusable. Use ellipsis to collapse the range.

  4. Not disabling Previous/Next at boundaries - Previous should be disabled on page 1. Next should be disabled on the last page. This provides clear affordance.

  5. Off-by-one errors in slicing - Data slicing is zero-based: start = (page - 1) * pageSize, end = start + pageSize. Page 1 starts at index 0.

Practice Questions

  1. How do you calculate the start and end index for a given page?
  2. Why should you reset to page 1 when the data array changes?
  3. How do you handle pagination with ellipsis for large page ranges?
  4. What is the difference between client-side and server-side pagination?
  5. How do you implement an items-per-page selector?

Challenge: Build a complete product catalog with client-side pagination, a page size selector (10/20/50), ellipsis page numbers, Previous/Next buttons, a search filter that resets pagination, and a "Results X-Y of Z" display.

FAQ

Should I use client-side or server-side pagination?

Client-side for <1000 items (fast, no server requests). Server-side for larger datasets (reduces bandwidth, supports database-level pagination).

How do I handle pagination with filtered data?

Store the filtered data separately and pass it to the pagination logic. Reset currentPage to 1 when the filter changes.

Can I use URL hash for pagination state?

Yes. Update window.location.hash = '#page-' + page on page change and read it on page load to restore the user's position.

How do I handle pagination with dynamic content loading?

Use a loading indicator, disable pagination buttons during load, and handle errors gracefully. Server-side pagination requires a loading state.

What is the best page size for pagination?

10-25 items per page for lists, 12-24 for image grids. Consider mobile users — smaller page sizes reduce scrolling.

Mini Project

Build a searchable, paginated product table with 200 sample products. Include a search input that filters products by name, a page size selector (10, 20, 50), pagination controls with ellipsis, and a summary line showing "Showing X-Y of Z products".

What's Next

Data presentation is key. Learn how jQuery effects add transitions and visual polish to pagination and other UI components.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro