# 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. - Published: 2026-04-21 - Updated: 2026-04-21 - Author: SharpSkill - Tags: angular, standalone-components, migration, tutorial - Reading time: 8 min --- 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. > **Contenido de este articulo** > > 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. ```typescript // hero-list.component.ts 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: `
@for (hero of heroes | search:query; track hero.id) { }
` }) export class HeroListComponent { heroes = signal([]); 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. ```bash # Step 1: Convert all declarations to standalone ng g @angular/core:standalone --path=src/app ``` Despues 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. ```bash # Step 2: Remove empty NgModules ng g @angular/core:standalone --path=src/app ``` El 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`. ```typescript // main.ts (after migration) 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. ```typescript // app.config.ts 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: ```typescript // app.routes.ts (before) 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: ```typescript // app.routes.ts (after) 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. ```typescript // settings/settings.routes.ts 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. ## 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. ```typescript // 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: `
` }) 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. ```json // tsconfig.json { "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. ```typescript // hero-list.component.spec.ts import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HeroListComponent } from './hero-list.component'; import { HeroService } from '../services/hero.service'; describe('HeroListComponent', () => { let fixture: ComponentFixture; 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. ## 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 `loadChildren` a `loadComponent` - Los SharedModules representan el principal bloqueo durante la migracion y deben descomponerse en declaraciones standalone individuales - Habilitar `strictStandalone` en 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/angular/angular-standalone-components-migration-best-practices