@defer blocks y triggers declarativos
Los @defer blocks representan una evolución natural del lazy loading tradicional a nivel de ruta, permitiendo ahora aplicar carga diferida de forma granular directamente en las plantillas. Esta característica de Angular 17+ complementa el lazy loading de rutas que ya conoces, ofreciendo control preciso sobre cuándo cargar componentes específicos dentro de una página.
Un @defer block encapsula cualquier contenido de plantilla que deseas cargar de forma diferida, desde componentes standalone hasta elementos HTML complejos. La sintaxis básica es simple y declarativa:
@defer {
<mi-componente-pesado />
}
Triggers declarativos disponibles
Angular proporciona múltiples triggers declarativos que determinan exactamente cuándo se debe cargar el contenido diferido. Estos triggers permiten optimizar la experiencia del usuario cargando contenido en el momento más apropiado.
Trigger idle
El trigger idle carga el contenido cuando el navegador está inactivo, siendo ideal para componentes no críticos:
@defer (idle) {
<panel-analytics />
}
Este trigger es perfecto para dashboards o widgets informativos que pueden esperar hasta que el usuario complete sus acciones principales.
Trigger viewport
El trigger viewport implementa lazy loading basado en visibilidad, cargando contenido cuando entra en el área visible:
@defer (viewport) {
<grafico-interactivo />
}
Especialmente útil para contenido "below-the-fold" como gráficos, imágenes complejas o secciones de contenido extenso.
Trigger interaction
El trigger interaction carga contenido en respuesta a la primera interacción del usuario con el elemento:
@defer (interaction) {
<formulario-complejo />
<editor-avanzado />
}
Ideal para formularios complejos, editores de texto o cualquier componente que requiera interacción activa del usuario.
Trigger hover
El trigger hover anticipa la intención del usuario cargando contenido al pasar el cursor:
@defer (hover) {
<tooltip-detallado />
<menu-contextual />
}
Perfecto para tooltips elaborados, menús contextuales o previsualizaciones que mejoran la experiencia sin impactar la carga inicial.
Trigger timer
El trigger timer permite retrasos temporales específicos, útil para contenido que debe aparecer después de un tiempo determinado:
@defer (timer(3000)) {
<mensaje-promocional />
}
El tiempo se especifica en milisegundos, siendo útil para notificaciones, mensajes promocionales o contenido que debe mostrarse después de que el usuario explore la página.
Trigger when
El trigger when ofrece control condicional basado en expresiones booleanas, integrándose naturalmente con signals:
@defer (when isFeatureEnabled()) {
<nueva-funcionalidad />
}
Este trigger es especialmente valioso para feature flags, contenido condicional basado en permisos de usuario, o componentes que dependen del estado de la aplicación.
Combinación de triggers
Los triggers pueden combinarse para crear estrategias de carga más sofisticadas usando operadores lógicos:
@defer (interaction; viewport) {
<componente-costoso />
}
En este ejemplo, el componente se carga cuando ocurre cualquiera de los dos eventos (OR lógico).
Para casos más específicos, puedes combinar triggers con condiciones:
@defer (when userLoggedIn() && viewport) {
<dashboard-usuario />
}
Requisitos para componentes standalone
Los @defer blocks funcionan exclusivamente con componentes standalone, lo cual se alinea perfectamente con la arquitectura moderna de Angular 20+. Los componentes deben declararse como standalone para ser elegibles para carga diferida:
@Component({
selector: 'mi-widget',
standalone: true,
imports: [CommonModule],
template: `<div>Contenido del widget</div>`
})
export class MiWidgetComponent { }
Esta restricción garantiza que el bundler pueda crear chunks separados correctamente y que la carga diferida funcione de manera predecible.
Casos de uso típicos
Los @defer blocks brillan en escenarios específicos donde el lazy loading granular aporta valor real:
- Widgets de dashboard que no son críticos para la funcionalidad principal
- Gráficos y visualizaciones que requieren librerías pesadas
- Contenido below-the-fold como secciones de testimonios o galerías
- Formularios complejos que solo se muestran bajo ciertas condiciones
- Componentes de terceros que agregan funcionalidad opcional
La clave está en identificar componentes que pueden cargarse después de la experiencia inicial sin impactar la funcionalidad core de tu aplicación.
@placeholder, @loading, @error y prefetch strategies
Los @defer blocks incluyen bloques auxiliares especializados para manejar los diferentes estados del ciclo de vida de la carga diferida. Estos bloques proporcionan control granular sobre la experiencia del usuario durante todo el proceso de carga.
Bloque @placeholder
El bloque @placeholder define qué mostrar antes de que se active el trigger del defer block. Este contenido aparece inmediatamente y debe ser ligero para no impactar el rendimiento inicial:
@defer (viewport) {
<grafico-complejo />
} @placeholder {
<div class="skeleton-chart">
<div class="skeleton-bar"></div>
<div class="skeleton-bar"></div>
<div class="skeleton-bar"></div>
</div>
}
El @placeholder es ideal para skeletons, espacios reservados visuales o mensajes informativos que indican al usuario que hay contenido disponible. Mantén este contenido simple y estático para preservar el beneficio de rendimiento del defer.
Bloque @loading
Una vez que el trigger se activa, el bloque @loading toma el control mientras se descarga y procesa el contenido diferido:
@defer (interaction) {
<editor-markdown />
} @placeholder {
<button>Cargar Editor</button>
} @loading {
<div class="loading-spinner">
<span>Cargando editor...</span>
</div>
}
El estado de loading es especialmente importante para componentes que tardan tiempo en descargarse o inicializarse. Proporciona feedback visual inmediato indicando que el proceso está en curso.
Bloque @error
El bloque @error maneja escenarios donde la carga diferida falla, ofreciendo una experiencia degradada elegante:
@defer (idle) {
<widget-externo />
} @placeholder {
<div class="widget-placeholder">Widget disponible</div>
} @loading {
<div>Cargando widget...</div>
} @error {
<div class="error-state">
<p>No se pudo cargar el widget</p>
<button (click)="reintentar()">Reintentar</button>
</div>
}
El manejo de errores es crucial para mantener la estabilidad de la aplicación cuando componentes externos o dependencias fallan al cargar.
Combinando todos los estados
Un @defer block completo con todos los estados proporciona una experiencia robusta:
@defer (when mostrarDashboard()) {
<dashboard-analytics
[datos]="datosAnalytics()"
[configuracion]="configDashboard()" />
} @placeholder {
<div class="dashboard-skeleton">
<div class="skeleton-header"></div>
<div class="skeleton-cards">
<div class="skeleton-card"></div>
<div class="skeleton-card"></div>
</div>
</div>
} @loading (minimum 500ms) {
<div class="loading-container">
<div class="spinner"></div>
<p>Preparando dashboard...</p>
</div>
} @error {
<div class="error-dashboard">
<h3>Dashboard no disponible</h3>
<p>Intenta recargar la página</p>
</div>
}
Nota el parámetro minimum en el bloque @loading, que garantiza que el estado de carga se muestre durante al menos 500ms, evitando flashes visuales molestos.
Estrategias de prefetch
Las prefetch strategies permiten precargar componentes antes de que se activen sus triggers, optimizando la percepción de velocidad:
Prefetch on idle
@defer (viewport; prefetch on idle) {
<mapa-interactivo />
} @placeholder {
<div class="mapa-placeholder">Mapa disponible</div>
}
Esta estrategia descarga el componente cuando el navegador está inactivo, pero no lo renderiza hasta que el trigger principal se active.
Prefetch on hover
@defer (interaction; prefetch on hover) {
<formulario-avanzado />
} @placeholder {
<button>Abrir formulario</button>
}
El prefetch on hover anticipa la interacción del usuario, descargando el componente cuando detecta intención pero renderizándolo solo en la interacción real.
Prefetch on timer
@defer (viewport; prefetch on timer(2000)) {
<galeria-imagenes />
} @placeholder {
<div class="galeria-preview">Vista previa disponible</div>
}
Esta estrategia programa la descarga después de un tiempo específico, útil para contenido que probablemente será necesario.
Testing de defer blocks
Angular proporciona DeferBlockFixture para testing de componentes con defer blocks:
it('debe mostrar placeholder inicialmente', async () => {
const fixture = TestBed.createComponent(MiComponente);
const deferBlock = fixture.debugElement.query(By.directive(DeferBlock));
expect(deferBlock.nativeElement.textContent).toContain('Contenido placeholder');
// Activar el defer block manualmente en tests
await deferBlock.trigger();
fixture.detectChanges();
expect(deferBlock.nativeElement.textContent).toContain('Contenido cargado');
});
Consideraciones de rendimiento
Las estrategias de prefetch deben usarse cuidadosamente para no anular los beneficios del lazy loading:
- Prefetch on idle es seguro para la mayoría de casos
- Prefetch on hover es efectivo para mejorar UX sin impacto significativo
- Prefetch on timer debe usarse con tiempos conservadores
La clave está en encontrar el equilibrio entre anticipar necesidades del usuario y conservar recursos del sistema.
Mejores prácticas para estados
- Placeholder: Mantén contenido estático y ligero
- Loading: Proporciona feedback claro del progreso
- Error: Ofrece opciones de recuperación cuando sea posible
- Prefetch: Usa estrategias conservadoras que no impacten la carga inicial
Los defer blocks con manejo completo de estados crean experiencias fluidas que se adaptan a diferentes condiciones de red y fallos potenciales, manteniendo la aplicación funcional y profesional.

Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Angular
Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender qué son los @defer blocks y su función en el lazy loading granular.
- Conocer los diferentes triggers declarativos disponibles para controlar la carga diferida.
- Aprender a manejar los estados de carga con @placeholder, @loading y @error.
- Entender las estrategias de prefetch para optimizar la carga de componentes.
- Aplicar buenas prácticas y testing en el uso de @defer blocks con componentes standalone.