DEV Community

Parth Raval
Parth Raval

Posted on

Load Data in Angular Using Route Resolvers Instead of "ngOnInit"

When building Angular applications, a common pattern for fetching data is calling an API inside the ngOnInit lifecycle hook of a component. While this works, it can lead to empty UIs that suddenly “pop” with data once the request completes. This can hurt UX and SEO.

A better approach is to load the data before the component is activated, using Angular’s Route Resolver. This ensures the component receives all necessary data immediately upon rendering.

Why Use a Resolver Instead of ngOnInit?

  • Better User Experience – No flicker or half-rendered screens.
  • Preloaded Data – Component starts with complete data.
  • Centralized Data Fetching – Keep data-loading logic separate from UI logic.
  • Error Handling at Route Level – Decide what happens if data fails before the component loads.

Step-by-Step Guide

1. Create a Service for Data Fetching

A typical Angular service using HttpClient to fetch data.

// src/app/services/user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UserService {
  private apiUrl = 'https://jsonplaceholder.typicode.com/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<any> {
    return this.http.get(this.apiUrl);
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Create a Route Resolver

The resolver implements Resolve and runs before the route activates.

// src/app/resolvers/user.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve } from '@angular/router';
import { Observable } from 'rxjs';
import { UserService } from '../services/user.service';

@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<any> {
  constructor(private userService: UserService) {}

  resolve(): Observable<any> {
    return this.userService.getUsers();
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Update Routing to Use the Resolver

We attach the resolver to a route so Angular fetches the data before navigating.

// src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserListComponent } from './components/user-list/user-list.component';
import { UserResolver } from './resolvers/user.resolver';

const routes: Routes = [
  {
    path: 'users',
    component: UserListComponent,
    resolve: { users: UserResolver }
  },
  { path: '', redirectTo: '/users', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}
Enter fullscreen mode Exit fullscreen mode

4. Access Resolved Data in the Component

Instead of calling the API in ngOnInit, we read it from ActivatedRoute.

// src/app/components/user-list/user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-user-list',
  template: `
    <h2>User List</h2>
    <ul>
      <li *ngFor="let user of users">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  `
})
export class UserListComponent implements OnInit {
  users: any[] = [];

  constructor(private route: ActivatedRoute) {}

  ngOnInit(): void {
    this.users = this.route.snapshot.data['users'];
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Run the Application

ng serve
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:4200/users and you’ll see the list of users load instantly, without any flicker.

✅ Advantages Over ngOnInit

Feature ngOnInit Resolver
Data loading happens after component render
Prevents UI flicker
Works well with SEO (Angular Universal)
Centralized fetching logic

Final Notes

  • Use resolvers for critical route data that must be available before the component loads.

  • For non-critical data (e.g., lazy-loaded sections), ngOnInit is still fine.

  • You can also handle loading indicators globally while the resolver works by subscribing to router events.

Top comments (2)

Collapse
 
dhutaryan profile image
Dzmitry Hutaryan

User opens the page but your request is freezing for some reason. No any indication for it. Looks like nothing has happen. I have one more question: how are you gonna refetch data if you needed?
I'm gonna say we have to think twice before using resolver.

Collapse
 
parthraval9 profile image
Parth Raval

Dzmitry Hutaryan, Thanks for your comment and sorry for the delayed response.

If the resolver request takes longer or fails, it's best to show a global loading indicator during data fetching to keep users informed.

For re-fetching data after the initial load, the component can manually call the service again, or the resolver can be triggered by route changes if needed.

Resolvers are ideal for loading critical data before the view appears

Some comments may only be visible to logged-in visitors. Sign in to view all comments.