Overview
The Pagination component provides accessible navigation through large sets of content. It supports multiple styles, sizes, and advanced features like page jumping, page size selection, and information display. The component is fully responsive and includes keyboard navigation and screen reader support.
🎨 Multiple Styles
Default, pills, rounded, borderless, outlined, and compact aiab-pagination variants.
📏 Flexible Sizing
Compact, default, and large aiab-pagination sizes for different interface densities.
♿ Full Accessibility
ARIA labels, keyboard navigation, focus management, and screen reader support.
📱 Responsive Design
Adaptive layouts that work on all screen sizes with smart content hiding.
🔧 Advanced Features
Page jumping, page size selection, info display, and loading states.
⚡ Performance Optimized
Efficient rendering and smooth interactions with minimal DOM manipulation.
Basic Pagination
Default Pagination
<nav aria-label="Pagination Navigation" role="navigation">
<ul class="aiab-pagination">
<li class="pagination__item pagination__item--nav">
<a href="#" class="pagination__link pagination__link--disabled" aria-label="Go to previous page">
<div class="pagination__nav">
<svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
<span class="pagination__nav-text">Previous</span>
</div>
</a>
</li>
<li class="pagination__item pagination__item--current">
<span class="pagination__current" aria-label="Page 1, current page" aria-current="page">1</span>
</li>
<li class="pagination__item">
<a href="#" class="pagination__link" aria-label="Go to page 2">2</a>
</li>
<li class="pagination__item">
<a href="#" class="pagination__link" aria-label="Go to page 3">3</a>
</li>
<!-- More pages... -->
<li class="pagination__item">
<span class="pagination__ellipsis" aria-hidden="true">…</span>
</li>
<li class="pagination__item">
<a href="#" class="pagination__link" aria-label="Go to page 10">10</a>
</li>
<li class="pagination__item pagination__item--nav">
<a href="#" class="pagination__link" aria-label="Go to next page">
<div class="pagination__nav">
<span class="pagination__nav-text">Next</span>
<svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</div>
</a>
</li>
</ul>
</nav>
Simple Pagination
<nav aria-label="Simple Pagination" role="navigation">
<ul class="aiab-pagination aiab-pagination--simple">
<li class="pagination__item">
<a href="#" class="pagination__link" aria-label="Go to previous page">
<div class="pagination__nav">
<svg class="pagination__nav-icon">...</svg>
<span class="pagination__nav-text">Previous</span>
</div>
</a>
</li>
<li class="pagination__item">
<a href="#" class="pagination__link" aria-label="Go to next page">
<div class="pagination__nav">
<span class="pagination__nav-text">Next</span>
<svg class="pagination__nav-icon">...</svg>
</div>
</a>
</li>
</ul>
</nav>
Pagination Variants
Pills Pagination
Rounded Pagination
Borderless Pagination
Outlined Pagination
<!-- Pills Pagination -->
<ul class="aiab-pagination aiab-pagination--pills">...</ul>
<!-- Rounded Pagination -->
<ul class="aiab-pagination aiab-pagination--rounded">...</ul>
<!-- Borderless Pagination -->
<ul class="aiab-pagination aiab-pagination--borderless">...</ul>
<!-- Outlined Pagination -->
<ul class="aiab-pagination aiab-pagination--outlined">...</ul>
Pagination Sizes
Compact Pagination
Default Pagination
Large Pagination
<!-- Compact Pagination -->
<ul class="aiab-pagination aiab-pagination--compact">...</ul>
<!-- Default Pagination -->
<ul class="aiab-pagination">...</ul>
<!-- Large Pagination -->
<ul class="aiab-pagination aiab-pagination--large">...</ul>
Advanced Pagination
Complete Pagination with Controls
<div class="aiab-pagination-container">
<div class="aiab-pagination-container__left">
<div class="pagination__info">
Showing <span class="pagination__info-highlight">1</span> to
<span class="pagination__info-highlight">10</span> of
<span class="pagination__info-highlight">97</span> results
</div>
</div>
<div class="aiab-pagination-container__center">
<nav aria-label="Pagination" role="navigation">
<ul class="aiab-pagination">
<!-- Pagination items... -->
</ul>
</nav>
</div>
<div class="aiab-pagination-container__right">
<div class="pagination__page-size">
<label for="page-size">Show:</label>
<select id="page-size" class="pagination__page-size-select">
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
</div>
<div class="pagination__jump">
<label for="jump-page">Go to:</label>
<input type="number" id="jump-page" class="pagination__jump-input" min="1" max="10">
<button class="pagination__jump-button">Go</button>
</div>
</div>
</div>
CSS Classes Reference
Base Classes
| Class | Description |
|---|---|
.aiab-pagination |
Base aiab-pagination aiab-container |
.pagination__item |
Individual aiab-pagination item |
.pagination__link |
Pagination link element |
.pagination__current |
Current page indicator |
.pagination__ellipsis |
Ellipsis for truncated pages |
.pagination__nav |
Navigation link aiab-container |
.pagination__nav-icon |
Navigation arrow icon |
.pagination__nav-text |
Navigation link text |
Style Variants
| Class | Description |
|---|---|
.aiab-pagination--simple |
Simple previous/next aiab-pagination |
.aiab-pagination--pills |
Pill-shaped aiab-pagination buttons |
.aiab-pagination--rounded |
Circular aiab-pagination buttons |
.aiab-pagination--borderless |
Pagination without borders |
.aiab-pagination--outlined |
Outlined aiab-pagination style |
Size Modifiers
| Class | Description |
|---|---|
.aiab-pagination--compact |
Compact aiab-pagination size |
.aiab-pagination--large |
Large aiab-pagination size |
State Classes
| Class | Description |
|---|---|
.pagination__link--disabled |
Disabled aiab-pagination link |
.aiab-pagination--loading |
Loading state aiab-pagination |
.pagination__item--nav |
Navigation item modifier |
.pagination__item--current |
Current page item modifier |
Advanced Feature Classes
| Class | Description |
|---|---|
.aiab-pagination-container |
Full aiab-pagination aiab-container with controls |
.pagination__info |
Pagination information display |
.pagination__info-highlight |
Highlighted information numbers |
.pagination__page-size |
Page size selector aiab-container |
.pagination__page-size-select |
Page size aiab-dropdown |
.pagination__jump |
Page jump aiab-container |
.pagination__jump-input |
Page jump input field |
.pagination__jump-button |
Page jump button |
JavaScript Integration
// Pagination component class
class Pagination {
constructor(options = {}) {
this.currentPage = options.currentPage || 1;
this.totalPages = options.totalPages || 1;
this.pageSize = options.pageSize || 10;
this.totalItems = options.totalItems || 0;
this.maxVisible = options.maxVisible || 5;
this.showInfo = options.showInfo !== false;
this.showJump = options.showJump || false;
this.showPageSize = options.showPageSize || false;
this.container = options.container;
this.onChange = options.onChange || (() => {});
this.init();
}
init() {
if (this.container) {
this.render();
this.attachEvents();
}
}
render() {
const paginationHTML = this.generateHTML();
this.container.innerHTML = paginationHTML;
}
generateHTML() {
const pages = this.generatePages();
const info = this.showInfo ? this.generateInfo() : '';
const pageSize = this.showPageSize ? this.generatePageSize() : '';
const jump = this.showJump ? this.generateJump() : '';
return `
<div class="aiab-pagination-container">
<div class="aiab-pagination-container__left">
${info}
</div>
<div class="aiab-pagination-container__center">
<nav aria-label="Pagination Navigation" role="navigation">
<ul class="aiab-pagination">
${this.generatePrevButton()}
${pages.map(page => this.generatePageItem(page)).join('')}
${this.generateNextButton()}
</ul>
</nav>
</div>
<div class="aiab-pagination-container__right">
${pageSize}
${jump}
</div>
</div>
`;
}
generatePages() {
const pages = [];
const half = Math.floor(this.maxVisible / 2);
let start = Math.max(1, this.currentPage - half);
let end = Math.min(this.totalPages, start + this.maxVisible - 1);
// Adjust start if we're near the end
if (end - start + 1 < this.maxVisible) {
start = Math.max(1, end - this.maxVisible + 1);
}
// Add first page and ellipsis if needed
if (start > 1) {
pages.push(1);
if (start > 2) {
pages.push('ellipsis');
}
}
// Add visible pages
for (let i = start; i <= end; i++) {
pages.push(i);
}
// Add ellipsis and last page if needed
if (end < this.totalPages) {
if (end < this.totalPages - 1) {
pages.push('ellipsis');
}
pages.push(this.totalPages);
}
return pages;
}
generatePageItem(page) {
if (page === 'ellipsis') {
return `
<li class="pagination__item">
<span class="pagination__ellipsis" aria-hidden="true">…</span>
</li>
`;
}
if (page === this.currentPage) {
return `
<li class="pagination__item pagination__item--current">
<span class="pagination__current" aria-label="Page ${page}, current page" aria-current="page">
${page}
</span>
</li>
`;
}
return `
<li class="pagination__item">
<a href="#" class="pagination__link" data-page="${page}" aria-label="Go to page ${page}">
${page}
</a>
</li>
`;
}
generatePrevButton() {
const disabled = this.currentPage === 1 ? 'pagination__link--disabled' : '';
const href = this.currentPage === 1 ? '#' : `#page-${this.currentPage - 1}`;
return `
<li class="pagination__item pagination__item--nav">
<a href="${href}" class="pagination__link ${disabled}" data-page="${this.currentPage - 1}" aria-label="Go to previous page">
<div class="pagination__nav">
<svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
<span class="pagination__nav-text">Previous</span>
</div>
</a>
</li>
`;
}
generateNextButton() {
const disabled = this.currentPage === this.totalPages ? 'pagination__link--disabled' : '';
const href = this.currentPage === this.totalPages ? '#' : `#page-${this.currentPage + 1}`;
return `
<li class="pagination__item pagination__item--nav">
<a href="${href}" class="pagination__link ${disabled}" data-page="${this.currentPage + 1}" aria-label="Go to next page">
<div class="pagination__nav">
<span class="pagination__nav-text">Next</span>
<svg class="pagination__nav-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</div>
</a>
</li>
`;
}
generateInfo() {
const start = ((this.currentPage - 1) * this.pageSize) + 1;
const end = Math.min(this.currentPage * this.pageSize, this.totalItems);
return `
<div class="pagination__info">
Showing <span class="pagination__info-highlight">${start}</span> to
<span class="pagination__info-highlight">${end}</span> of
<span class="pagination__info-highlight">${this.totalItems}</span> results
</div>
`;
}
generatePageSize() {
const options = [10, 25, 50, 100];
const optionsHTML = options.map(size =>
`<option value="${size}" ${size === this.pageSize ? 'selected' : ''}>${size}</option>`
).join('');
return `
<div class="pagination__page-size">
<label for="page-size-select">Show:</label>
<select id="page-size-select" class="pagination__page-size-select">
${optionsHTML}
</select>
</div>
`;
}
generateJump() {
return `
<div class="pagination__jump">
<label for="jump-page-input">Go to:</label>
<input type="number" id="jump-page-input" class="pagination__jump-input"
min="1" max="${this.totalPages}" placeholder="${this.currentPage}">
<button class="pagination__jump-button">Go</button>
</div>
`;
}
attachEvents() {
this.container.addEventListener('click', (e) => {
e.preventDefault();
const link = e.target.closest('.pagination__link:not(.pagination__link--disabled)');
const jumpButton = e.target.closest('.pagination__jump-button');
if (link) {
const page = parseInt(link.dataset.page);
if (page >= 1 && page <= this.totalPages) {
this.goToPage(page);
}
} else if (jumpButton) {
const input = this.container.querySelector('.pagination__jump-input');
const page = parseInt(input.value);
if (page >= 1 && page <= this.totalPages) {
this.goToPage(page);
}
}
});
this.container.addEventListener('change', (e) => {
if (e.target.classList.contains('pagination__page-size-select')) {
const newPageSize = parseInt(e.target.value);
this.updatePageSize(newPageSize);
}
});
// Keyboard navigation
this.container.addEventListener('keydown', (e) => {
if (e.target.classList.contains('pagination__jump-input') && e.key === 'Enter') {
const page = parseInt(e.target.value);
if (page >= 1 && page <= this.totalPages) {
this.goToPage(page);
}
}
});
}
goToPage(page) {
if (page !== this.currentPage && page >= 1 && page <= this.totalPages) {
this.currentPage = page;
this.render();
this.attachEvents();
this.onChange({
currentPage: this.currentPage,
pageSize: this.pageSize
});
}
}
updatePageSize(newPageSize) {
this.pageSize = newPageSize;
this.totalPages = Math.ceil(this.totalItems / this.pageSize);
this.currentPage = 1; // Reset to first page
this.render();
this.attachEvents();
this.onChange({
currentPage: this.currentPage,
pageSize: this.pageSize
});
}
// Public methods
update(options) {
Object.assign(this, options);
this.totalPages = Math.ceil(this.totalItems / this.pageSize);
this.render();
this.attachEvents();
}
}
// Usage example
const pagination = new Pagination({
aiab-container: document.getElementById('aiab-pagination-container'),
currentPage: 1,
totalItems: 97,
pageSize: 10,
maxVisible: 5,
showInfo: true,
showJump: true,
showPageSize: true,
onChange: (data) => {
console.log('Page changed:', data);
// Fetch new data here
}
});
Best Practices
Accessibility Guidelines
- ARIA labels: Use descriptive aria-label attributes for screen readers
- Current page indication: Use aria-current="page" for the active page
- Keyboard navigation: Ensure all interactive elements are keyboard accessible
- Focus management: Provide clear focus indicators and logical tab order
UX Considerations
- Page count: Show 5-7 page numbers maximum for optimal usability
- Clear labeling: Use "Previous/Next" labels, not just arrow symbols
- Mobile adaptation: Hide page numbers on small screens, keep prev/next
- Loading states: Provide visual feedback during page transitions
Performance Tips
- Efficient rendering: Only re-render when necessary
- Debounced inputs: Debounce page jump input handling
- URL synchronization: Keep aiab-pagination state in sync with URL
- Server-side aiab-pagination: Don't load all data at once for large datasets
Implementation Notes
- Semantic HTML: Use proper navigation and list elements
- Responsive design: Adapt aiab-pagination for different screen sizes
- State management: Keep aiab-pagination state consistent across components
- Error handling: Gracefully handle invalid page numbers and network errors