DEV Community

Cover image for ๐Ÿ”„ Smart HTTP Loader in Angular Using Interceptors โ€“ Show One Loader for All API Calls
Suliman Munawar khan
Suliman Munawar khan

Posted on

๐Ÿ”„ Smart HTTP Loader in Angular Using Interceptors โ€“ Show One Loader for All API Calls

Ever faced this?

  • You make two or more HTTP requests in Angular
  • Multiple loaders show up โ€” and flicker
  • Or the loader hides before all calls are done

Letโ€™s fix that! In this article, Iโ€™ll walk you through how to build a global, smart HTTP loader using Angular interceptors. Itโ€™ll:

โœ… Show just one loader for any number of API calls

โœ… Stay visible until all requests finish

โœ… Make your app feel polished and professional


๐Ÿง  Whatโ€™s the Problem?

Angular apps often make multiple HTTP calls โ€” maybe to load user data, dashboard stats, and notifications.

But if each call has its own loader logic, the UI becomes a mess:

  • Loaders flash on and off
  • Users think the app is done loading when it's not
  • Code becomes hard to manage

Letโ€™s centralize this using HTTP interceptors and a shared LoaderService.


๐Ÿ”ง Step-by-Step Implementation


1๏ธโƒฃ Create the LoaderService

This service will track the number of active requests.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class LoaderService {
  private requestCount = 0;
  public isLoading$ = new BehaviorSubject<boolean>(false);

  show() {
    this.requestCount++;
    if (this.requestCount === 1) {
      this.isLoading$.next(true);
    }
  }

  hide() {
    if (this.requestCount > 0) {
      this.requestCount--;
    }

    if (this.requestCount === 0) {
      this.isLoading$.next(false);
    }
  }

  reset() {
    this.requestCount = 0;
    this.isLoading$.next(false);
  }
}
Enter fullscreen mode Exit fullscreen mode

2๏ธโƒฃ Create the HTTP Interceptor

This interceptor will automatically call show() and hide().

// loader.interceptor.ts
import { Injectable } from '@angular/core';
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpEvent,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoaderService } from './loader.service';

@Injectable()
export class LoaderInterceptor implements HttpInterceptor {
  constructor(private loaderService: LoaderService) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    this.loaderService.show();

    return next.handle(req).pipe(
      finalize(() => this.loaderService.hide())
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก Key point: The finalize() operator ensures hide() is called whether the request succeeds or fails.


3๏ธโƒฃ Register the Interceptor

Add it to your app module:

// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { LoaderInterceptor } from './loader.interceptor';

@NgModule({
  // ...
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: LoaderInterceptor,
      multi: true,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

4๏ธโƒฃ Show the Loader in the UI

You can now show a spinner component anywhere using the isLoading$ observable.

Hereโ€™s a simple example using Angular Material:

<!-- app.component.html -->
<mat-progress-bar
  *ngIf="isLoading$ | async"
  mode="indeterminate"
  color="primary"
></mat-progress-bar>

<router-outlet></router-outlet>
Enter fullscreen mode Exit fullscreen mode
// app.component.ts
import { Component } from '@angular/core';
import { LoaderService } from './loader.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  isLoading$ = this.loaderService.isLoading$;

  constructor(private loaderService: LoaderService) {}
}
Enter fullscreen mode Exit fullscreen mode

โœ… Output Demo

When multiple requests fire:

  • Youโ€™ll see one smooth loader
  • It will disappear only when all requests complete
  • Works across any part of your app

๐Ÿ›  Tips and Enhancements

  • Delay loader show by 100ms to avoid flashing for fast calls
  • Add an exclude list inside the interceptor to skip loader for certain URLs (e.g. logging, analytics)
  • Debounce or throttle UI updates if youโ€™re tracking many rapid requests

๐Ÿงช Bonus: ngx-loading-bar (Optional Lib)

Prefer a library? ngx-loading-bar integrates with Angular interceptors out of the box.

npm install @ngx-loading-bar/core @ngx-loading-bar/http-client
Enter fullscreen mode Exit fullscreen mode
// app.module.ts
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client';

@NgModule({
  imports: [LoadingBarHttpClientModule],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Drop this in your layout:

<ngx-loading-bar></ngx-loading-bar>
Enter fullscreen mode Exit fullscreen mode

Boom ๐Ÿ’ฅ Instant loader for all HTTP requests.

๐Ÿ Conclusion

You now have a professional, smart loader system that just works ๐Ÿ”

Itโ€™s:

  • Centralized
  • Lightweight
  • Easy to maintain

No more copy-pasting loader logic all over your components!

๐Ÿ‘‹ About Me

Iโ€™m a frontend developer working with Angular, React, and AI-assisted tools. I love writing clean, scalable code and sharing what I learn along the way.

Letโ€™s connect in the comments โ€” and donโ€™t forget to โค๏ธ the post if you found it useful!


Would you like me to write the next article in this Angular Pro Tips series on dynamic dashboards with charts?

Top comments (0)