Router Signals en Angular 21: navegación standalone sin cargar todo el Router
Siempre que necesitaba saber si una ruta estaba activa en Angular, inyectaba el Router completo y me suscribía a los eventos. En componentes simples de navegación, era matar moscas a cañonazos. Angular 21 introduce funciones standalone que devuelven Signals — cargás exactamente lo que necesitás, sin más.
El problema con inyectar el Router entero
// ❌ Antes: inyectar todo el Router para saber si una ruta está activa
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({ ... })
export class SidebarComponent implements OnInit, OnDestroy {
isDashboard = false;
private sub = Subscription.EMPTY;
constructor(private router: Router) {}
ngOnInit() {
this.sub = this.router.events
.pipe(filter(e => e instanceof NavigationEnd))
.subscribe(() => {
this.isDashboard = this.router.url === '/dashboard';
});
}
ngOnDestroy() { this.sub.unsubscribe(); }
}
isActive(): la función standalone con Signal
// ✅ Angular 21: función standalone, devuelve Signal
import { Component } from '@angular/core';
import { isActive } from '@angular/router';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-sidebar',
standalone: true,
imports: [RouterLink],
template: `
<nav>
<a routerLink="/dashboard" [class.active]="isDashboard()">Dashboard</a>
<a routerLink="/usuarios" [class.active]="isUsuarios()">Usuarios</a>
<a routerLink="/reportes" [class.active]="isReportes()">Reportes</a>
</nav>
`
})
export class SidebarComponent {
isDashboard = isActive('/dashboard', { exact: true });
isUsuarios = isActive('/usuarios');
isReportes = isActive('/reportes');
}
Otras funciones standalone del Router
import { isActive, currentRoute, routeParams, queryParams, routeData } from '@angular/router';
@Component({ ... })
export class DetalleComponent {
// Parámetros de ruta como Signal — sin ActivatedRoute
params = routeParams();
usuarioId = computed(() => this.params()['id']);
// Query params como Signal
qParams = queryParams();
pagina = computed(() => Number(this.qParams()['page'] ?? 1));
// Data estática de la ruta
data = routeData();
titulo = computed(() => this.data()['titulo'] ?? 'Sin título');
}
Navigation API experimental (v21.0)
// app.config.ts — habilitar Navigation API experimental
import { provideExperimentalNavigationApi } from '@angular/common';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideExperimentalNavigationApi()
]
};
Location strategies: control sobre el trailing slash (v21.2)
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withRouterConfig({
trailingSlash: 'never' // 'always' | 'never' | 'preserve'
}))
]
};
Por qué el tree-shaking importa
Con las funciones standalone, el bundler puede eliminar del bundle final todo el código del Router que no se usa. Si solo usás isActive y routeParams, el resto no viaja al usuario. En aplicaciones grandes esto puede reducir el bundle inicial de forma significativa — especialmente relevante en conexiones móviles lentas.
← Artículo anterior: Vitest reemplaza a Karma y Angular habla con tu IA: el nuevo ecosistema de herramientas | Serie Angular 20 → 21.2 | Próximo: Guía práctica: cómo migré un proyecto de Angular 19/20 a Angular 21.2 sin morir en el intento →

Dejar un comentario
¿Quieres unirte a la conversación?Siéntete libre de contribuir!