Optimizing Angular Applications with Advanced Routing Strategies

Table of contents
- ๐ Introduction
- ๐งญ1. Lazy Loading: Loading Only What You Need
- ๐2. Preloading: Preparing for Future Navigation
- ๐ Alternative: Custom Preloading Strategy
- ๐3. Route Guards: Controlling Access
- ๐งฉ4. Nested Routes: Organizing Your Application
- ๐5. Dynamic Component Loading: Flexibility at Runtime
- ๐6. Network-Aware Preloading: Smart Resource Loading
- โ Summary of Loading Strategies
- ๐ Conclusion

๐ Introduction
Angular's routing system helps developers build single-page applications with multiple views. By using advanced techniques like lazy loading, preloading, route guards, and nested routes, you can make your application faster, more secure, and easier to maintain.
In this article, we'll explore these strategies with simple explanations and examples, so you can apply them to your Angular projects.
๐งญ1. Lazy Loading: Loading Only What You Need
What is it?
Lazy loading means loading parts of your application only when they're needed, rather than loading everything upfront. This helps in reducing the initial load time of your application.
Example:
Imagine your app has a "Profile" section. With lazy loading, the code for the "Profile" section is only loaded when the user navigates to it, not when the app starts.
const routes: Routes = [
{
path: 'feature',
loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}
];
Why use it?
It makes your app load faster initially, improving the user experience.
๐2. Preloading: Preparing for Future Navigation
What is it?
Preloading involves loading certain parts of your application in the background after the initial load, so they're ready when the user navigates to them.
Built-in Strategies:
Preloading: Loads modules in the background after the initial load to speed up navigation.
PreloadAllModules: Preloads all lazy-loaded modules.
Custom Preloading: Allows you to specify which modules to preload based on conditions.
Example:
If your app has a "Settings" page, preloading can load the "Settings" module in the background while the user is interacting with other parts of the app.
import { PreloadingStrategy, Route } from '@angular/router';
1. Setting Up Lazy-Loaded Modules
const routes: Routes =
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule)
}
];
2. Applying Preloading Strategy
In your AppRoutingModule, you can use Angular's built-in PreloadAllModules strategy to preload all lazy-loaded modules after the initial load:
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
],
exports: [RouterModule]
})
export class AppRoutingModule {}
With this setup SettingsModule will be preloaded in the background after the initial application load, making future navigation to these sections faster.
๐ Alternative: Custom Preloading Strategy
If you want more control over which modules to preload, you can implement a custom preloading strategy. For example, preload only based on a condition:
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
// Preload only if the route has 'preload' set to true
return route.data && route.data['preload'] ? load() : of(null);
}
}
Then, in your routing configuration:
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule),
data: { preload: true }
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule),
data: { preload: false }
}
];
In this setup, only the DashboardModule will be preloaded, as specified by the preload: true flag.
Why use it?
It ensures that when the user decides to visit the "Settings" page, it loads instantly without delay.
๐3. Route Guards: Controlling Access
What are they?
Route guards are like security checks that determine whether a user can access a particular route or not.
Types of Route Guards:
CanActivate: Determines if a route can be activated.
CanDeactivate: Determines if a route can be deactivated.
CanLoad: Determines if a module can be loaded.
Example:
Before allowing a user to access the Dashboard page, a route guard can check if the user is logged in. If not, it can redirect them to the login page.
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(): boolean {
if (this.authService.isAuthenticated()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
Usage in Routing Module:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard]
}
];
Why use them?
They help in implementing features like authentication and authorization, ensuring that users can only access pages they're permitted to.
๐งฉ4. Nested Routes: Organizing Your Application
What are they?
Nested routes allow you to define routes within other routes, creating a hierarchy. This is useful for organizing your application into sections and subsections.
Example:
In a blogging application, you might have a "Posts" section. Under "Posts," you could have routes like "All Posts" and "Create Post."
const routes: Routes = [
{
path: 'courses',
component: CoursesComponent,
children: [
{
path: ':id',
component: CourseDetailComponent
}
]
}
];
Why use them?
They help in structuring your application in a logical and organized manner, making it easier to manage and navigate.
๐5. Dynamic Component Loading: Flexibility at Runtime
What is it?
Dynamic component loading allows you to load components at runtime based on certain conditions, rather than at compile time.
Example:
In a dashboard application, you might want to display different widgets based on user preferences. Dynamic component loading allows you to load the appropriate widget component when needed.
@Component({
selector: 'app-dynamic',
template: '<ng-container #container></ng-container>'
})
export class DynamicComponent {
@ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
loadComponent(component: Type<any>) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(component);
this.container.createComponent(componentFactory);
}
}
Why use it?
It provides flexibility in rendering components, allowing you to build more dynamic and customizable user interfaces.
๐6. Network-Aware Preloading: Smart Resource Loading
What is it?
Network-aware preloading adjusts the preloading behavior based on the user's network conditions, ensuring efficient use of resources.
Example:
If the user's device is on a slow network, the application can delay or skip preloading certain modules to conserve bandwidth.
@Injectable({ providedIn: 'root' })
export class NetworkAwarePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
const connection = navigator.connection;
if (connection && connection.saveData) {
return of(null);
}
return load();
}
}
Usage in Routing Module:
RouterModule.forRoot(routes, {
preloadingStrategy: NetworkAwarePreloadingStrategy
})
Why use it?
It optimizes resource loading, providing a better user experience, especially for users with limited or slow internet connections.
โ Summary of Loading Strategies
Strategy | Description | Suitable For |
Eager Loading | Modules loaded at application startup | Small applications |
Lazy Loading | Modules loaded on demand when route is activated | Large applications |
Preloading | Modules loaded in the background after application starts | Improving UX in large applications |
Conditional Loading | Modules loaded based on conditions (e.g., user roles, device type) | Role-based or device-specific content |
Dynamic Component Loading | Components loaded dynamically at runtime | Scenarios with unknown components at compile time |
Network-Aware Preloading | Preloading adjusted based on network conditions | Users with varying network speeds |
๐ Conclusion
Mastering advanced routing techniques in Angular, such as lazy loading, preloading, route guards, nested routes, and dynamic component loading is essential for building efficient, scalable, and maintainable applications. By implementing these strategies, developers can enhance performance, improve user experience, and maintain a clean and organized codebase.
As Angular continues to evolve, staying updated with the latest routing features and best practices will empower you to leverage the full potential of the framework. Remember, the key to optimizing your Angular application lies in thoughtful routing design and strategic implementation.
If you found this guide helpful, leave a like โค๏ธ or share it!
Subscribe to my newsletter
Read articles from Pranali Kulkarni directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
