Componentes Standalone en Angular: Migracion desde NgModules y Buenas Practicas en 2026
Guia completa para migrar aplicaciones Angular de NgModules a componentes standalone. Incluye el proceso CLI en 3 pasos, lazy loading con loadComponent, manejo de SharedModules, pruebas unitarias, optimizacion de rendimiento y mejores practicas para desarrollo standalone-only en 2026.

Los componentes standalone representan el cambio arquitectonico mas significativo en Angular desde la introduccion del framework. A partir de Angular 21, los NgModules dejaron de ser necesarios para organizar declaraciones, y el esquema de migracion oficial del CLI automatiza la mayor parte del proceso. Esta guia detalla cada paso de la migracion, las estrategias para manejar dependencias compartidas y las mejoras medibles en rendimiento que se obtienen al adoptar la arquitectura standalone.
Migracion completa de NgModules a standalone con el CLI oficial en 3 pasos, conversion de rutas con loadComponent, eliminacion de SharedModules, configuracion de strictStandalone, comparativas de rendimiento y pruebas unitarias simplificadas para Angular 21.
NgModules vs Componentes Standalone: Que Cambio
Durante anios, los NgModules constituyeron la unidad fundamental de organizacion en Angular. Cada componente, directiva o pipe requeria ser declarado dentro de un modulo, y ese modulo debia importar otros modulos para acceder a sus declaraciones. Este modelo introducia una capa de indireccion que complicaba el tree-shaking, generaba archivos boilerplate y dificultaba la comprension del grafo de dependencias real de la aplicacion.
Los componentes standalone eliminan esa capa intermedia. Un componente standalone declara directamente sus dependencias a traves de la propiedad imports dentro del decorador @Component. No necesita pertenecer a ningun modulo. Sus dependencias son explicitas, verificables por el compilador y optimizables por el bundler.
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeroCardComponent } from './hero-card.component';
import { SearchPipe } from '../pipes/search.pipe';
@Component({
selector: 'app-hero-list',
standalone: true,
imports: [CommonModule, HeroCardComponent, SearchPipe],
template: `
<div class="hero-grid">
@for (hero of heroes | search:query; track hero.id) {
<app-hero-card [hero]="hero" />
}
</div>
`
})
export class HeroListComponent {
heroes = signal<Hero[]>([]);
query = signal('');
}En este ejemplo, HeroListComponent importa directamente CommonModule, HeroCardComponent y SearchPipe. El compilador de Angular verifica en tiempo de compilacion que cada dependencia esta disponible. Si se elimina una importacion no utilizada, el bundler excluye ese codigo del bundle final. Con NgModules, esa optimizacion era imposible porque el modulo reexportaba bloques completos de declaraciones sin distinguir cuales se utilizaban realmente.
El Proceso de Migracion CLI en 3 Pasos
Angular proporciona un schematic oficial que automatiza entre el 80% y el 90% de la migracion. El proceso se ejecuta en tres fases secuenciales, cada una con un proposito especifico.
Paso 1: Convertir Declaraciones a Standalone
El primer paso analiza todos los componentes, directivas y pipes declarados en NgModules y les agrega la propiedad standalone: true. Tambien actualiza la propiedad imports de cada componente para incluir las dependencias que antes se resolvian a traves del modulo contenedor.
# Step 1: Convert all declarations to standalone
ng g @angular/core:standalone --path=src/appDespues de ejecutar este comando, cada componente funciona de manera independiente. Los NgModules aun existen, pero sus declaraciones ya no dependen de ellos para resolver dependencias. Es recomendable ejecutar las pruebas unitarias despues de este paso para verificar que la conversion fue correcta antes de continuar.
Paso 2: Eliminar NgModules Vacios
Una vez que todas las declaraciones son standalone, los NgModules que solo servian como contenedores de declaraciones quedan vacios. El segundo paso del schematic identifica y elimina esos modulos residuales.
# Step 2: Remove empty NgModules
ng g @angular/core:standalone --path=src/appEl schematic preserva los modulos que contienen providers o configuracion especifica, ya que esos aun cumplen una funcion. Solo elimina los modulos cuya unica razon de existir era agrupar declaraciones.
Paso 3: Bootstrap sin NgModules
El paso final reemplaza el bootstrap basado en modulos (platformBrowserDynamic().bootstrapModule(AppModule)) por el bootstrap directo con bootstrapApplication. La configuracion que antes residia en AppModule se traslada a un objeto ApplicationConfig.
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error(err));El archivo app.config.ts centraliza los providers globales de la aplicacion: router, HttpClient, interceptors, animaciones y cualquier otro servicio que deba estar disponible en toda la aplicacion.
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor]))
]
};Este patron de configuracion resulta mas explicito que el anterior basado en modulos. Cada provider se registra a traves de una funcion dedicada (provideRouter, provideHttpClient), lo que facilita identificar exactamente que servicios estan disponibles a nivel global y permite al bundler optimizar mejor el codigo final.
Migracion de Rutas: De Modulos a loadComponent
La conversion del sistema de rutas requiere intervencion manual en la mayoria de los proyectos. El schematic del CLI no modifica las definiciones de rutas automaticamente, por lo que es necesario reemplazar loadChildren con loadComponent o con la nueva variante de loadChildren basada en archivos de rutas.
Antes de la migracion, las rutas con lazy loading apuntaban a modulos:
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module')
.then(m => m.DashboardModule)
}
];Despues de la migracion, las rutas apuntan directamente a componentes o a archivos de rutas:
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component')
.then(c => c.DashboardComponent)
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.routes')
.then(r => r.settingsRoutes)
}
];Para secciones con subrutas, la estrategia optima consiste en crear archivos de rutas independientes que exporten un arreglo de tipo Routes. Cada subruta utiliza loadComponent para mantener el lazy loading granular.
import { Routes } from '@angular/router';
export const settingsRoutes: Routes = [
{
path: '',
loadComponent: () => import('./settings.component')
.then(c => c.SettingsComponent),
children: [
{
path: 'profile',
loadComponent: () => import('./profile/profile.component')
.then(c => c.ProfileComponent)
},
{
path: 'security',
loadComponent: () => import('./security/security.component')
.then(c => c.SecurityComponent)
}
]
}
];Este modelo de archivos de rutas independientes facilita la organizacion por feature. Cada seccion de la aplicacion maneja sus propias rutas, y el archivo principal app.routes.ts solo contiene referencias de nivel superior.
¿Listo para aprobar tus entrevistas de Angular?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Manejo de SharedModules y Dependencias Comunes
Los SharedModules representan el principal obstaculo durante la migracion a standalone. En la mayoria de los proyectos Angular, existe al menos un SharedModule que agrupa componentes de UI reutilizables, directivas y pipes, y los reexporta para que cualquier feature module pueda importarlos.
Con componentes standalone, el concepto de SharedModule pierde sentido. Cada declaracion compartida se convierte en un componente standalone independiente que se importa directamente donde se necesita.
// Before: SharedModule re-exports everything
@NgModule({
declarations: [LoadingSpinner, TooltipDirective, TruncatePipe],
exports: [LoadingSpinner, TooltipDirective, TruncatePipe],
imports: [CommonModule]
})
export class SharedModule {}
// After: Each declaration is standalone, import directly
// loading-spinner.component.ts
@Component({
selector: 'app-loading-spinner',
standalone: true,
template: `<div class="spinner" role="status"></div>`
})
export class LoadingSpinner {}La estrategia recomendada para proyectos grandes es migrar el SharedModule de forma incremental. Primero, convertir cada declaracion del SharedModule a standalone sin eliminar el modulo. Luego, actualizar los consumidores uno por uno para que importen directamente las declaraciones standalone. Finalmente, cuando ningun modulo dependa del SharedModule, eliminarlo.
Para facilitar la transicion, es posible crear un archivo barrel (shared/index.ts) que reexporte todas las declaraciones compartidas. Los consumidores importan desde ese barrel, manteniendo una sintaxis limpia sin depender de un NgModule.
Aplicar el Modo Standalone Exclusivo
Una vez completada la migracion, es fundamental evitar que nuevos NgModules se introduzcan en el proyecto. Angular proporciona una opcion de compilador que bloquea la creacion de componentes no standalone.
{
"angularCompilerOptions": {
"strictStandalone": true
}
}Con strictStandalone habilitado, el compilador genera un error si un componente no incluye standalone: true en su decorador. Esta configuracion funciona como una barrera de proteccion que garantiza la consistencia arquitectonica del proyecto a largo plazo.
Tambien es recomendable agregar reglas de linting personalizadas que detecten importaciones de NgModules residuales y patrones de codigo que contradigan la arquitectura standalone.
Mejoras de Rendimiento: Bundle Size y Lazy Loading
La migracion a standalone produce mejoras significativas y medibles en el rendimiento de la aplicacion. Las siguientes metricas corresponden a una aplicacion de produccion de tamanio mediano (aproximadamente 120 componentes) antes y despues de la migracion.
| Metric | NgModule-based | Standalone | Improvement | |--------|---------------|------------|-------------| | Initial bundle | 485 KB | 218 KB | -55% | | Largest lazy chunk | 142 KB | 38 KB | -73% | | Time to Interactive | 3.2s | 1.8s | -44% | | Build time (esbuild) | 12.4s | 8.1s | -35% |
La reduccion del bundle inicial se explica por el tree-shaking mejorado. Con NgModules, el bundler incluia todas las declaraciones de un modulo importado, aunque solo se utilizara una. Con standalone, el grafo de dependencias es exacto: solo se incluye lo que un componente importa directamente.
La reduccion en el tamanio de los lazy chunks es aun mas pronunciada. Cuando cada ruta carga un componente individual en lugar de un modulo completo con todas sus declaraciones, los chunks resultantes contienen exclusivamente el codigo necesario para esa vista.
El tiempo de build tambien mejora porque esbuild puede paralelizar mejor la compilacion cuando las unidades de compilacion son componentes individuales en lugar de modulos que agrupan multiples archivos.
Pruebas de Componentes Standalone
Una de las ventajas mas apreciadas de los componentes standalone es la simplificacion del setup de pruebas unitarias. Con NgModules, configurar TestBed requeria importar o mockear el modulo completo donde estaba declarado el componente, junto con todos sus modulos dependientes. Con standalone, el componente se importa directamente y solo se proveen los servicios que necesita.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroListComponent } from './hero-list.component';
import { HeroService } from '../services/hero.service';
describe('HeroListComponent', () => {
let fixture: ComponentFixture<HeroListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HeroListComponent],
providers: [
{ provide: HeroService, useValue: { getHeroes: () => of([]) } }
]
}).compileComponents();
fixture = TestBed.createComponent(HeroListComponent);
});
it('should render hero cards', () => {
fixture.componentRef.setInput('heroes', mockHeroes);
fixture.detectChanges();
const cards = fixture.nativeElement.querySelectorAll('app-hero-card');
expect(cards.length).toBe(mockHeroes.length);
});
});El cambio principal esta en configureTestingModule: el componente se coloca en imports en lugar de declarations. Esta distincion refleja que el componente standalone es autosuficiente y maneja sus propias dependencias. Solo es necesario proveer los servicios inyectados que se desean mockear.
Este patron reduce la cantidad de codigo boilerplate en las pruebas, disminuye el acoplamiento entre el test y la estructura modular de la aplicacion, y hace que los tests sean mas resilientes a refactorizaciones estructurales.
Errores Comunes durante la Migracion
La migracion a standalone, aunque automatizada en gran parte, presenta varios puntos donde es frecuente cometer errores.
Olvidar convertir pipes y directivas. El schematic del CLI convierte componentes, pero en ocasiones omite pipes o directivas que no estan directamente referenciados en templates. Es necesario verificar manualmente que todas las declaraciones tengan standalone: true.
Importaciones circulares. Cuando dos componentes standalone se importan mutuamente, se genera una dependencia circular que rompe la compilacion. La solucion es extraer la logica compartida a un tercer componente o servicio que ambos importen sin crear ciclos.
Providers duplicados. Al migrar de NgModules que proporcionaban servicios con forRoot(), es posible registrar el mismo provider en multiples lugares. Con bootstrapApplication, los providers globales se registran una sola vez en app.config.ts. Los providers locales a una ruta se registran en la definicion de la ruta.
No actualizar las rutas. El schematic no modifica las definiciones de rutas. Si las rutas siguen apuntando a modulos eliminados, la aplicacion falla en tiempo de ejecucion. Es necesario revisar cada ruta y convertir loadChildren a loadComponent donde corresponda.
Mantener CommonModule innecesariamente. A partir de Angular 19, las directivas y pipes mas comunes (NgIf, NgFor, DatePipe, etc.) estan disponibles de forma global sin importar CommonModule. Mantener esa importacion no causa errores pero agrega dependencias innecesarias al grafo del componente.
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Conclusion
La migracion de NgModules a componentes standalone en Angular es un proceso bien definido que produce beneficios concretos en mantenibilidad, rendimiento y experiencia de desarrollo. Los puntos clave a considerar son los siguientes:
- El schematic oficial del CLI automatiza entre el 80% y el 90% de la conversion de declaraciones a standalone
- Los modulos de rutas requieren conversion manual de
loadChildrenaloadComponent - Los SharedModules representan el principal bloqueo durante la migracion y deben descomponerse en declaraciones standalone individuales
- Habilitar
strictStandaloneen tsconfig previene la reintroduccion de NgModules en el proyecto - Los bundles iniciales se reducen entre un 30% y un 55% gracias al tree-shaking mejorado
- Las pruebas unitarias se simplifican considerablemente al eliminar la necesidad de importar modulos completos en TestBed
- La deteccion de cambios zoneless de Angular 21 y la reactividad basada en signals se integran de forma natural con la arquitectura standalone, preparando las aplicaciones para el proximo paradigma de reactividad del framework
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Etiquetas
Compartir
Artículos relacionados

Angular 18: Signals y Nuevas Funcionalidades
Angular 18 estabiliza Signals como primitiva reactiva, introduce APIs basadas en signals y habilita la deteccion de cambios sin Zone.js para aplicaciones mas performantes.

Laravel 11: Construir una Aplicacion Completa desde Cero
Guia completa para construir una aplicacion con Laravel 11: autenticacion, API REST, Eloquent ORM y despliegue a produccion. Tutorial practico para desarrolladores principiantes e intermedios.

Expo Router en React Native: Guia Completa de Navegacion Basada en Archivos
Guia completa de Expo Router para React Native en 2026. Navegacion basada en archivos, rutas dinamicas, tabs, modales, typed routes, middleware y proteccion de rutas con ejemplos de codigo.