π Smarter Infinite Scroll in Angular with IntersectionObserver


In my previous blog, I demonstrated how to implement infinite scroll in Angular using scroll events. While that method works well, there's a cleaner and more performant approach available the IntersectionObserver API.
In this post, Iβll walk you through how to use IntersectionObserver to implement infinite scroll in Angular without relying on scroll events or calculating positions manually.
π Why Use IntersectionObserver?
The IntersectionObserver API lets you detect when an element (called a sentinel) enters the viewport. This makes it perfect for loading more data automatically with no scroll math involved.
β Benefits:
Browser-optimized
Cleaner and more declarative
No need for debouncing or throttle logic
Decoupled from scroll listeners
π οΈ Implementation in Angular
π§± Component Setup
We'll start with a scrollable container and use a special <div> at the bottom as our sentinel element.
HTML Template:
<div class="container">
<div *ngFor="let item of items" class="item">
{{ item }}
</div>
<div *ngIf="isLoading" class="loading">Loading...</div>
<div *ngIf="endOfData" class="end">No more items</div>
<!-- Sentinel element -->
<div #anchor class="sentinel" *ngIf="!endOfData"></div>
</div>
Component Code:
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { PaginationService } from 'src/app/services/pagination.service';
@Component({
selector: 'app-infinite-scroll-intersection-observer',
templateUrl: './infinite-scroll-intersection-observer.component.html',
styleUrls: ['./infinite-scroll-intersection-observer.component.css']
})
export class InfiniteScrollIntersectionObserverComponent implements OnInit, AfterViewInit, OnDestroy {
items: string[] = [];
page = 1;
pageSize = 20;
isLoading = false;
endOfData = false;
@ViewChild('anchor', { static: false }) anchor!: ElementRef;
private observer!: IntersectionObserver;
constructor(private paginationService: PaginationService) {}
ngOnInit() {
this.loadItems();
}
ngAfterViewInit() {
this.setupIntersectionObserver();
}
ngOnDestroy() {
if (this.observer) {
this.observer.disconnect();
}
}
private setupIntersectionObserver() {
const options = {
root: null, // observes viewport
threshold: 0.1
};
this.observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting && !this.isLoading && !this.endOfData) {
this.loadItems();
}
}, options);
if (this.anchor) {
this.observer.observe(this.anchor.nativeElement);
}
}
loadItems() {
if (this.isLoading || this.endOfData) return;
this.isLoading = true;
this.paginationService.getItems(this.page, this.pageSize).subscribe(data => {
if (data.length === 0) {
this.endOfData = true;
this.observer.disconnect(); // Optional: stop observing
} else {
this.items.push(...data);
this.page++;
}
this.isLoading = false;
});
}
}
π Styling Tips
Make sure your container is scrollable and your sentinel is visible to the observer:
.container {
height: 400px;
overflow-y: auto;
}
.sentinel {
height: 1px;
}
.loading, .end {
text-align: center;
padding: 10px;
}
π Backend Note
π‘ Note: The backend API (pagination service) used in this example is the same as in my previous blog post on Infinite Scroll with Angular and .NET Core API.
If you're looking for how the data is fetched or how the pagination service is set up, please refer to the earlier post here.
β Summary
Using IntersectionObserver:
Makes infinite scroll cleaner and more efficient
Lets the browser handle intersection logic instead of manual calculations
Reduces complexity in your code
π¬ Final Thought
If youβre building Angular apps that fetch data as the user scrolls, I highly recommend giving IntersectionObserver a try.
β‘οΈ Already implemented it?
β‘οΈ Prefer scroll event listeners instead?
Let me know what worked for you!
Subscribe to my newsletter
Read articles from Pranali Kulkarni directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
