Back

Creating a Pagination with Ellipsis for Many Pages

Learn how to build a compact pagination component that handles large numbers of pages using ellipsis.

HTML

Let's create a pagination component that can handle a large number of pages using ellipsis. Here's the HTML structure we'll start with:

        <nav class="pagination" aria-label="Page navigation">
          <button class="pagination-item" aria-label="Go to previous page" id="prev-button">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <polyline points="15 18 9 12 15 6"></polyline>
            </svg>
          </button>
          
          <div class="pagination-list" id="pagination-list">
            <button class="pagination-item">1</button>
            <button class="pagination-item">2</button>
            <button class="pagination-item active">3</button>
            <span class="pagination-ellipsis">…</span>
            <button class="pagination-item">8</button>
            <button class="pagination-item">9</button>
            <button class="pagination-item">10</button>
          </div>
          
          <button class="pagination-item" aria-label="Go to next page" id="next-button">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
              <polyline points="9 18 15 12 9 6"></polyline>
            </svg>
          </button>
        </nav>
      
Key elements of our HTML structure: - We use a semantic nav element with appropriate ARIA labels for accessibility - Previous and next buttons with clear SVG icons - A container for the page numbers and ellipsis - The ellipsis is represented by the HTML entity &hellip; - The current page has an "active" class

JavaScript

Now let's implement the JavaScript to dynamically generate the pagination based on the current page and total pages:

        document.addEventListener('DOMContentLoaded', () => {
          const prevButton = document.getElementById('prev-button');
          const nextButton = document.getElementById('next-button');
          const paginationList = document.getElementById('pagination-list');
          
          // Configuration
          const totalPages = 10;
          let currentPage = 3;
          
          function updatePagination() {
            // Clear current pagination list
            paginationList.innerHTML = '';
            
            // Helper function to create a page button
            function createPageButton(pageNum, isActive = false) {
              const button = document.createElement('button');
              button.className = `pagination-item${isActive ? ' active' : ''}`;
              button.textContent = pageNum;
              button.addEventListener('click', () => setCurrentPage(pageNum));
              button.disabled = isActive;
              return button;
            }
            
            // Helper function to create an ellipsis
            function createEllipsis() {
              const span = document.createElement('span');
              span.className = 'pagination-ellipsis';
              span.innerHTML = '…';
              return span;
            }
            
            // Always show first page
            paginationList.appendChild(createPageButton(1, currentPage === 1));
            
            // Logic for showing correct pagination items with ellipsis
            if (currentPage > 3) {
              paginationList.appendChild(createEllipsis());
            }
            
            // Show pages around current page
            const startPage = Math.max(2, currentPage - 1);
            const endPage = Math.min(totalPages - 1, currentPage + 1);
            
            for (let i = startPage; i <= endPage; i++) {
              if (i <= totalPages - 1 && i >= 2) {
                paginationList.appendChild(createPageButton(i, currentPage === i));
              }
            }
            
            // Add ellipsis before last page if needed
            if (currentPage < totalPages - 2) {
              paginationList.appendChild(createEllipsis());
            }
            
            // Always show last page
            if (totalPages > 1) {
              paginationList.appendChild(createPageButton(totalPages, currentPage === totalPages));
            }
            
            // Update prev/next button states
            prevButton.disabled = currentPage === 1;
            nextButton.disabled = currentPage === totalPages;
          }
          
          // Function to set current page and update UI
          function setCurrentPage(pageNum) {
            currentPage = pageNum;
            updatePagination();
            // In a real application, you would fetch data for the new page here
            console.log(`Navigated to page ${currentPage}`);
          }
          
          // Add event listeners to prev/next buttons
          prevButton.addEventListener('click', () => {
            if (currentPage > 1) {
              setCurrentPage(currentPage - 1);
            }
          });
          
          nextButton.addEventListener('click', () => {
            if (currentPage < totalPages) {
              setCurrentPage(currentPage + 1);
            }
          });
          
          // Initialize pagination
          updatePagination();
        });
      
The key features of our JavaScript implementation: - Dynamically generates pagination based on current page position - Always shows the first and last page - Shows a small window of pages around the current page - Uses ellipsis to indicate skipped pages when there are many pages - Handles navigation with previous and next buttons - Updates the UI whenever the current page changes

CSS

Now let's style our pagination component to make it visually appealing:

        .pagination {
          --text-color: #374151;
          --background: white;
          --border-color: #e5e7eb;
          --hover-bg: #f9fafb;
          --active-bg: #4f46e5;
          --active-text: white;
          --disabled-color: #d1d5db;
          
          display: flex;
          align-items: center;
          gap: 6px;
          font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
          padding: 8px 12px;
          border-radius: 8px;
          background: var(--background);
        }

        .pagination-list {
          display: flex;
          align-items: center;
          gap: 4px;
        }

        .pagination-item {
          display: flex;
          align-items: center;
          justify-content: center;
          min-width: 36px;
          height: 36px;
          padding: 0 8px;
          border: 1px solid var(--border-color);
          border-radius: 6px;
          background: var(--background);
          color: var(--text-color);
          font-size: 14px;
          font-weight: 500;
          cursor: pointer;
          transition: all 0.2s ease;
        }
      
Let's style the different states of our pagination items:

        .pagination-item:hover:not(:disabled):not(.active) {
          background: var(--hover-bg);
          border-color: #d1d5db;
        }

        .pagination-item.active {
          background: var(--active-bg);
          color: var(--active-text);
          border-color: var(--active-bg);
          cursor: default;
        }

        .pagination-item:focus-visible {
          outline: 2px solid var(--active-bg);
          outline-offset: 2px;
        }

        .pagination-item:disabled {
          opacity: 0.6;
          cursor: not-allowed;
        }

        .pagination-ellipsis {
          display: flex;
          align-items: center;
          justify-content: center;
          width: 36px;
          height: 36px;
          font-size: 18px;
          color: var(--text-color);
          user-select: none;
        }
      
Finally, let's make our pagination responsive for smaller screens:

        /* Responsive styles */
        @media (max-width: 480px) {
          .pagination {
            padding: 6px 8px;
          }
          
          .pagination-item {
            min-width: 32px;
            height: 32px;
            font-size: 13px;
          }
          
          .pagination-ellipsis {
            width: 24px;
          }
        }
      
This pagination component with ellipsis offers several benefits: - Scalability: Works well with any number of pages, from a few to hundreds - Space efficiency: The ellipsis keeps the pagination compact even with many pages - Context awareness: Always shows the user where they are in relation to the first and last page - Accessibility: Uses semantic HTML and proper ARIA attributes for screen readers - Visual clarity: Clear visual hierarchy with distinct active state and hover effects - Adaptability: Responsive design works across different screen sizes This pattern is ideal for search results, large product catalogs, document libraries, or any application with paginated content that might span many pages.

Whole code

<nav class="pagination" aria-label="Page navigation">
  <button class="pagination-item" aria-label="Go to previous page" id="prev-button">
    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <polyline points="15 18 9 12 15 6"></polyline>
    </svg>
  </button>
  
  <div class="pagination-list" id="pagination-list">
    <button class="pagination-item">1</button>
    <button class="pagination-item">2</button>
    <button class="pagination-item active">3</button>
    <span class="pagination-ellipsis">&hellip;</span>
    <button class="pagination-item">8</button>
    <button class="pagination-item">9</button>
    <button class="pagination-item">10</button>
  </div>
  
  <button class="pagination-item" aria-label="Go to next page" id="next-button">
    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <polyline points="9 18 15 12 9 6"></polyline>
    </svg>
  </button>
</nav>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const prevButton = document.getElementById('prev-button');
  const nextButton = document.getElementById('next-button');
  const paginationList = document.getElementById('pagination-list');
  
  // Configuration
  const totalPages = 10;
  let currentPage = 3;
  
  function updatePagination() {
    // Clear current pagination list
    paginationList.innerHTML = '';
    
    // Helper function to create a page button
    function createPageButton(pageNum, isActive = false) {
      const button = document.createElement('button');
      button.className = `pagination-item${isActive ? ' active' : ''}`;
      button.textContent = pageNum;
      button.addEventListener('click', () => setCurrentPage(pageNum));
      button.disabled = isActive;
      return button;
    }
    
    // Helper function to create an ellipsis
    function createEllipsis() {
      const span = document.createElement('span');
      span.className = 'pagination-ellipsis';
      span.innerHTML = '&hellip;';
      return span;
    }
    
    // Always show first page
    paginationList.appendChild(createPageButton(1, currentPage === 1));
    
    // Logic for showing correct pagination items with ellipsis
    if (currentPage > 3) {
      paginationList.appendChild(createEllipsis());
    }
    
    // Show pages around current page
    const startPage = Math.max(2, currentPage - 1);
    const endPage = Math.min(totalPages - 1, currentPage + 1);
    
    for (let i = startPage; i <= endPage; i++) {
      if (i <= totalPages - 1 && i >= 2) {
        paginationList.appendChild(createPageButton(i, currentPage === i));
      }
    }
    
    // Add ellipsis before last page if needed
    if (currentPage < totalPages - 2) {
      paginationList.appendChild(createEllipsis());
    }
    
    // Always show last page
    if (totalPages > 1) {
      paginationList.appendChild(createPageButton(totalPages, currentPage === totalPages));
    }
    
    // Update prev/next button states
    prevButton.disabled = currentPage === 1;
    nextButton.disabled = currentPage === totalPages;
  }
  
  // Function to set current page and update UI
  function setCurrentPage(pageNum) {
    currentPage = pageNum;
    updatePagination();
    // In a real application, you would fetch data for the new page here
    console.log(`Navigated to page ${currentPage}`);
  }
  
  // Add event listeners to prev/next buttons
  prevButton.addEventListener('click', () => {
    if (currentPage > 1) {
      setCurrentPage(currentPage - 1);
    }
  });
  
  nextButton.addEventListener('click', () => {
    if (currentPage < totalPages) {
      setCurrentPage(currentPage + 1);
    }
  });
  
  // Initialize pagination
  updatePagination();
});
</script>

<style>
.pagination {
  --text-color: #374151;
  --background: white;
  --border-color: #e5e7eb;
  --hover-bg: #f9fafb;
  --active-bg: #4f46e5;
  --active-text: white;
  --disabled-color: #d1d5db;
  
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  padding: 8px 12px;
  border-radius: 8px;
  background: var(--background);
}

.pagination-list {
  display: flex;
  align-items: center;
  gap: 4px;
}

.pagination-item {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  border: 1px solid var(--border-color);
  border-radius: 6px;
  background: var(--background);
  color: var(--text-color);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.pagination-item:hover:not(:disabled):not(.active) {
  background: var(--hover-bg);
  border-color: #d1d5db;
}

.pagination-item.active {
  background: var(--active-bg);
  color: var(--active-text);
  border-color: var(--active-bg);
  cursor: default;
}

.pagination-item:focus-visible {
  outline: 2px solid var(--active-bg);
  outline-offset: 2px;
}

.pagination-item:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.pagination-ellipsis {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  font-size: 18px;
  color: var(--text-color);
  user-select: none;
}

/* Responsive styles */
@media (max-width: 480px) {
  .pagination {
    padding: 6px 8px;
  }
  
  .pagination-item {
    min-width: 32px;
    height: 32px;
    font-size: 13px;
  }
  
  .pagination-ellipsis {
    width: 24px;
  }
}
</style>
      
Thank you for reading this article.

Comments

More paginations

Similar

See also