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 (1)

Collapse
 
xhanimanolistrungu profile image
Xhani Manolis Trungu

Hi @sulimanmunawarkhan , I just wrote an article about interceptors and I think it complements yours (even though mine is for Angular 20)

⚡ Conditional Interceptors in Angular 20