Server Side Pagination In Angular With Ngx Pagination

Learn how to implement efficient server-side pagination in Angular applications using the ngx-pagination library, with practical code examples and best practices.

Why Server-Side Pagination Matters

Modern Angular applications often need to display large datasets to users. Fetching and rendering thousands of records at once creates significant performance bottlenecks that degrade user experience and increase server load. Server-side pagination solves this problem by retrieving only the data needed for the current page view, dramatically improving both frontend performance and backend scalability.

The ngx-pagination library provides a robust, well-tested solution for implementing pagination in Angular applications. This comprehensive guide walks through the complete implementation of server-side pagination, from installation through advanced configuration, with practical code examples you can apply to your own projects.

For developers working with JavaScript and TypeScript fundamentals, understanding pagination patterns like these builds on core concepts of data handling and state management.

What you'll learn:

  • Understanding client-side vs server-side pagination approaches
  • Installing and configuring ngx-pagination in Angular projects
  • Building a pagination service layer with proper TypeScript interfaces
  • Implementing component logic with query parameter handling
  • Creating templates with pagination controls
  • Best practices for production implementations

Understanding Server-Side vs Client-Side Pagination

Before diving into implementation details, it is essential to understand the fundamental differences between server-side and client-side pagination approaches, as this decision impacts application architecture, performance characteristics, and user experience.

Client-Side Pagination Overview

Client-side pagination loads the entire dataset from the server in a single request, then divides the data into pages within the browser. This approach works well for small to medium datasets where all data can transfer quickly and store in memory without performance issues. The primary advantage is instant page switching--once the initial load completes, users navigate between pages without additional network requests.

However, client-side pagination has significant limitations when dealing with large datasets. Initial load time increases proportionally with data volume, browser memory consumption grows substantially, and mobile users may encounter performance degradation or crashes.

Server-Side Pagination Overview

Server-side pagination delegates the paging logic to the backend, which returns only the specific subset of records requested for each page view. The client sends page number and page size parameters with each request, receiving a response containing only the current page's data along with metadata about total available records and total page count.

This approach offers several compelling advantages for production applications. Initial load time remains fast regardless of dataset size because only a small data slice transfers over the network. Memory usage stays consistent regardless of total data volume, making the application suitable for devices with limited resources. Server load distributes more evenly across requests rather than concentrating on a single massive data transfer.

Server-Side vs Client-Side Pagination Comparison
AspectServer-Side PaginationClient-Side Pagination
Initial LoadFast--only first page loadsSlow--entire dataset transfers
Memory UsageConsistent and lowGrows with dataset size
Page TransitionsRequires network requestInstant after initial load
ScalabilityHandles millions of recordsLimited by client resources
Network EfficiencyOptimal--minimal data transferHigher total bandwidth usage
Backend RequirementsPagination API support neededSimpler backend implementation

Installing and Configuring Ngx-Pagination

The ngx-pagination library integrates seamlessly with Angular applications through npm installation and Angular module configuration.

Installation

Begin by installing the library in your Angular project using npm or yarn:

npm install ngx-pagination --save

The --save flag ensures the dependency records in your package.json, making the library version part of your project's dependency management.

Module Import and Registration

After installation, import the NgxPaginationModule in your Angular module file:

import { NgxPaginationModule } from 'ngx-pagination';

@NgModule({
 imports: [
 NgxPaginationModule
 ]
})
export class AppModule { }

For Angular 17+ standalone components:

import { NgxPaginationModule } from 'ngx-pagination';

@Component({
 selector: 'app-root',
 standalone: true,
 imports: [NgxPaginationModule],
 templateUrl: './app.component.html'
})
export class AppComponent { }

Configuration Options

The ngx-pagination library provides extensive configuration options through the PaginationControlsComponent and the paginate pipe.

paginationConfig = {
 itemsPerPage: 10,
 currentPage: 1,
 totalItems: 0
};

When implementing pagination in your frontend stack, proper configuration like this ensures optimal user experience across different screen sizes and devices.

Building the Pagination Service Layer

A well-structured pagination implementation separates concerns between data fetching, state management, and presentation.

Defining the Data Model

Start by defining TypeScript interfaces that match your API response structure:

export interface PaginatedResponse<T> {
 data: T[];
 total: number;
 page: number;
 per_page: number;
 total_pages: number;
}

export interface Passenger {
 _id: string;
 name: string;
 trips: number;
 airline: Airline[];
}

export interface Airline {
 _id: string;
 name: string;
 country: string;
 logo: string;
 slogan: string;
}

Creating the Data Service

Implement a service that handles API communication with pagination parameters:

@Injectable({
 providedIn: 'root'
})
export class PassengerService {
 private apiUrl = 'https://api.example.com/passengers';

 constructor(private http: HttpClient) {}

 getPassengers(page: number, itemsPerPage: number): Observable<PaginatedResponse<Passenger>> {
 const params = new HttpParams()
 .set('page', page.toString())
 .set('size', itemsPerPage.toString());

 return this.http.get<PaginatedResponse<Passenger>>(this.apiUrl, { params });
 }
}

This pattern of building reusable services with proper TypeScript interfaces reflects functional programming principles similar to what you might explore when learning about JavaScript currying and composable data transformations.

Implementing the Component Logic

The Angular component manages pagination state, responds to page changes, and coordinates between the service layer and the template.

Component State Management

@Component({
 selector: 'app-passenger-list',
 templateUrl: './passenger-list.component.html'
})
export class PassengerListComponent implements OnInit {
 passengers: Passenger[] = [];
 paginationConfig = {
 currentPage: 1,
 itemsPerPage: 10,
 totalItems: 0
 };

 constructor(
 private passengerService: PassengerService,
 private activatedRoute: ActivatedRoute,
 private router: Router
 ) {}
}

Handling Query Parameters

Use ActivatedRoute to read page numbers from the URL query parameters:

ngOnInit(): void {
 this.activatedRoute.queryParams.subscribe(params => {
 this.paginationConfig.currentPage = params['page'] ? parseInt(params['page'], 10) : 1;
 this.loadPassengers();
 });
}

private loadPassengers(): void {
 this.passengerService.getPassengers(
 this.paginationConfig.currentPage,
 this.paginationConfig.itemsPerPage
 ).subscribe(response => {
 this.passengers = response.data;
 this.paginationConfig.totalItems = response.total;
 });
}

Managing Page Changes

onPageChange(newPage: number): void {
 this.router.navigate([], {
 queryParams: { page: newPage },
 queryParamsHandling: 'merge'
 });

 this.passengerService.getPassengers(
 newPage,
 this.paginationConfig.itemsPerPage
 ).subscribe(response => {
 this.passengers = response.data;
 this.paginationConfig.totalItems = response.total;
 });
}

Creating the Template Markup

Displaying Paginated Data

<div class="passenger-table">
 <table>
 <thead>
 <tr>
 <th>Name</th>
 <th>Trips</th>
 <th>Airline</th>
 </tr>
 </thead>
 <tbody>
 <tr *ngFor="let passenger of passengers">
 <td>{{ passenger.name }}</td>
 <td>{{ passenger.trips }}</td>
 <td>{{ passenger.airline[0]?.name }}</td>
 </tr>
 </tbody>
 </table>
</div>

Adding Pagination Controls

<div class="pagination-container">
 <pagination-controls
 [id]="'passengerPagination'"
 [maxSize]="5"
 [directionLinks]="true"
 [previousLabel]="'Previous'"
 [nextLabel]="'Next'"
 (pageChange)="onPageChange($event)">
 </pagination-controls>
</div>

The id property uniquely identifies this pagination instance, which matters when you have multiple paginated lists on the same page. The maxSize property limits visible page numbers, preventing pagination controls from becoming unwieldy with many pages.

Advanced Configuration and Customization

Custom Control Labels

Customize control labels to support internationalization:

<pagination-controls
 [id]="'customPagination'"
 [previousLabel]="'Précédent'"
 [nextLabel]="'Suivant'"
 [firstLabel]="'Première'"
 [lastLabel]="'Dernière'">
</pagination-controls>

Responsive Behavior

Consider how pagination controls display on different screen sizes:

.pagination-controls {
 display: flex;
 justify-content: center;
 gap: 0.5rem;
}

@media (max-width: 768px) {
 .pagination-controls {
 font-size: 0.875rem;
 }
}

Best Practices for Production Implementations

Error Handling

Network requests can fail for various reasons, and your pagination implementation should handle these gracefully:

private loadPassengers(): void {
 this.passengerService.getPassengers(
 this.paginationConfig.currentPage,
 this.paginationConfig.itemsPerPage
 ).subscribe({
 next: (response) => {
 this.passengers = response.data;
 this.paginationConfig.totalItems = response.total;
 },
 error: (error) => {
 console.error('Failed to load passengers:', error);
 this.errorMessage = 'Unable to load data. Please try again.';
 }
 });
}

Loading State Indication

Provide visual feedback during data fetching:

isLoading = false;

private loadPassengers(): void {
 this.isLoading = true;
 // ... fetch data
 this.isLoading = false;
}
<div class="loading-indicator" *ngIf="isLoading">
 <span class="spinner"></span>
 Loading data...
</div>

Maintaining Scroll Position

When users navigate between pages, scroll to the top of the list:

onPageChange(newPage: number): void {
 this.router.navigate([], {
 queryParams: { page: newPage },
 queryParamsHandling: 'merge'
 });

 const listElement = document.querySelector('.passenger-table');
 if (listElement) {
 listElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
 }
}

Our web development services team specializes in building scalable Angular applications with production-ready implementations like this pagination pattern, ensuring optimal performance for your users.

Conclusion

Server-side pagination with ngx-pagination provides a robust foundation for displaying large datasets in Angular applications. By fetching only the required data from your backend, you create responsive applications that scale gracefully regardless of dataset size.

The implementation follows Angular best practices with proper separation of concerns, type safety through TypeScript interfaces, and URL-driven state management for shareable deep links. The ngx-pagination library handles the complexity of pagination control rendering while remaining customizable enough for specialized requirements.

Key takeaways:

  • Server-side pagination is essential for applications with large datasets
  • The ngx-pagination library provides comprehensive pagination functionality
  • Proper error handling and loading states improve user experience
  • URL-based state management enables deep linking and bookmarkable pages

Frequently Asked Questions

When should I use server-side pagination instead of client-side?

Use server-side pagination when dealing with large datasets (thousands+ records), when memory efficiency matters, when you need deep linking to specific pages, or when backend API already supports pagination parameters.

Can ngx-pagination handle multiple paginated lists on one page?

Yes, use unique 'id' attributes for each pagination-controls component to differentiate between multiple paginated lists on the same page.

How do I preserve pagination state during navigation?

Use Angular's Router with query parameters to store the current page number. This enables bookmarkable URLs and preserves state across page refreshes.

What if my API doesn't support pagination?

If your API returns all data, implement pagination on the client side using the paginate pipe. For production applications, consider adding pagination support to your backend API.

Need Help Building Angular Applications?

Our team of Angular experts can help you implement efficient pagination, optimize performance, and build scalable web applications.