# Angular Standalone Components: migrazione e best practice nel 2026 > Guida completa alla migrazione di applicazioni Angular dai NgModule agli standalone component. Copre la migrazione ufficiale CLI in 3 passaggi, lazy loading, routing e best practice per Angular 21. - Published: 2026-04-21 - Updated: 2026-04-27 - Author: SharpSkill - Tags: angular, standalone components, migration, angular 21, ngmodule, lazy loading - Reading time: 9 min --- Gli standalone component di Angular eliminano la necessità dei NgModule, riducono il boilerplate e abilitano un lazy loading a grana fine in tutta l'applicazione. Da quando Angular 19 ha reso lo standalone il default e Angular 21 ha consolidato la change detection zoneless, migrare codebase legacy basate su moduli è diventato sia semplice sia ad alto impatto. > **Punto chiave** > > Lo schematic ufficiale dell'Angular CLI gestisce automaticamente la maggior parte della migrazione in tre passaggi. Una tipica applicazione enterprise può completare la conversione in un singolo sprint, con bundle che si riducono del 30-50 % grazie al lazy loading per componente. ## NgModule vs standalone component: cosa è cambiato I NgModule hanno funzionato come contesto di compilazione per i componenti fin da Angular 2. Ogni componente, direttiva e pipe doveva essere dichiarato in esattamente un modulo, e la condivisione di funzionalità richiedeva import ed export di moduli orchestrati con cura. Ne risultava un forte accoppiamento tra feature non correlate e un tree-shaking difficoltoso. Gli standalone component ribaltano questo modello. Ogni componente dichiara le proprie dipendenze direttamente nell'array `imports` del decoratore `@Component`. Niente registrazione nei moduli, niente moduli condivisi, niente barrel export di mezza applicazione. ```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(''); } ``` L'array `imports` sostituisce l'intero grafo delle dipendenze dei NgModule. Il bundler vede esattamente quali componenti, pipe e direttive servono a ciascun file, abilitando un tree-shaking preciso. ## Il processo di migrazione CLI in 3 passaggi Angular fornisce uno schematic automatizzato che gestisce la migrazione in tre passaggi sequenziali. Ogni step si basa sul precedente, e tra un passaggio e l'altro il progetto deve compilare senza errori. ### Passaggio 1: convertire le dichiarazioni in standalone Il primo passaggio analizza ogni componente, direttiva e pipe del progetto, aggiunge `standalone: true` e sposta gli import necessari dal NgModule padre nell'array `imports` di ciascun componente. ```bash # Step 1: Convert all declarations to standalone ng g @angular/core:standalone --path=src/app ``` Alla richiesta, selezionare **"Convert all components, directives and pipes to standalone"**. Lo schematic usa l'analisi statica per risolvere le dipendenze, quindi qualunque componente con metadata non analizzabili a build time verrà saltato con un warning. ### Passaggio 2: rimuovere i NgModule non necessari Con tutte le dichiarazioni ora standalone, molti NgModule diventano gusci vuoti. Questo passaggio identifica i moduli che si limitavano a riesportare dichiarazioni standalone e li rimuove. ```bash # Step 2: Remove empty NgModules ng g @angular/core:standalone --path=src/app ``` Selezionare **"Remove unnecessary NgModule classes"**. I moduli che contengono ancora provider, configurazioni di rotte o sono importati da più moduli vengono mantenuti con un commento TODO per la revisione manuale. ### Passaggio 3: passare al bootstrap standalone L'ultimo passaggio sostituisce il NgModule root con l'API `bootstrapApplication` di Angular e converte il componente root in standalone. ```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)); ``` ```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])) ] }; ``` Il pattern `ApplicationConfig` sostituisce gli array `providers` e `imports` del modulo root. Tutte le funzioni provider (`provideRouter`, `provideHttpClient`, `provideAnimations`) funzionano direttamente senza wrapper modulari. ## Migrazione del routing: dai moduli a loadComponent I moduli di routing richiedono attenzione manuale perché lo schematic non converte automaticamente gli import di `loadChildren` di moduli in `loadComponent` o in `loadChildren` a livello di rotta con rotte standalone. Il pattern legacy caricava interi moduli di feature: ```typescript // app.routes.ts (before) const routes: Routes = [ { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module') .then(m => m.DashboardModule) } ]; ``` L'equivalente standalone carica direttamente singoli componenti o file di rotte: ```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) } ]; ``` ```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) } ] } ]; ``` `loadComponent` carica in lazy un singolo componente. `loadChildren` con un file di rotte carica in lazy un'intera area di feature. Entrambi producono chunk separati che il browser scarica su richiesta. ## Gestire SharedModule e dipendenze comuni Gli SharedModule — i moduli contenitore che esportano componenti, direttive e pipe di uso comune — sono il blocco più frequente durante la migrazione. Lo schematic non riesce a rimuoverli automaticamente perché molti moduli li importano. La soluzione: convertire le dichiarazioni condivise in standalone una a una, poi eliminare lo SharedModule quando nessuno lo importa più. ```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 {} ``` I consumatori importano direttamente `LoadingSpinner` invece dell'intero SharedModule. Il bundler include solo i componenti effettivamente necessari a ciascuna rotta. ## Imporre lo sviluppo solo standalone Dopo la migrazione è essenziale impedire che nuovi NgModule rientrino nella codebase. Angular fornisce un'opzione del compilatore TypeScript dedicata. ```json // tsconfig.json { "angularCompilerOptions": { "strictStandalone": true } } ``` Con `strictStandalone` attivo, ogni tentativo di creare un componente, direttiva o pipe non standalone produce un errore di compilazione. Questo impone la nuova architettura a tutto il team. ## Guadagni di performance: dimensioni del bundle e lazy loading Il principale beneficio prestazionale degli standalone component arriva dal lazy loading granulare. Con i NgModule il lazy loading agiva a livello di modulo: importare un componente da un modulo trascinava ogni dichiarazione esportata da quel modulo. Gli standalone component spezzano questo accoppiamento. Un benchmark reale su un'applicazione enterprise di media dimensione (oltre 200 componenti) ha mostrato: | Metrica | Basato su NgModule | Standalone | Miglioramento | |--------|---------------|------------|-------------| | Bundle iniziale | 485 KB | 218 KB | -55 % | | Chunk lazy più grande | 142 KB | 38 KB | -73 % | | Time to Interactive | 3,2 s | 1,8 s | -44 % | | Tempo di build (esbuild) | 12,4 s | 8,1 s | -35 % | Questi numeri derivano dalla rimozione dell'overhead di risoluzione dei moduli e dalla capacità del bundler di eliminare gli export inutilizzati a livello di componente invece che a livello di modulo. ## Testare gli standalone component Gli unit test si semplificano notevolmente con gli standalone component. La configurazione di `TestBed` non richiede più l'import di interi moduli per soddisfare le dipendenze di un componente. ```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); }); }); ``` Il componente entra direttamente nell'array `imports` di `TestBed.configureTestingModule`. Tutte le dipendenze dichiarate sono già risolte tramite il proprio `imports`, quindi non servono import aggiuntivi di moduli. ## Errori comuni durante la migrazione **Import circolari tra standalone component.** Quando il componente A importa il B e B importa A, il compilatore TypeScript solleva un errore di dipendenza circolare. La soluzione: estrarre l'interfaccia condivisa in un file separato oppure usare `forwardRef()` come workaround temporaneo durante il refactoring della catena di dipendenze. **Librerie di terze parti che usano ancora NgModule.** Molte librerie sono migrate a standalone, ma alcuni pacchetti legacy esportano ancora NgModule. Questi moduli vanno importati direttamente nell'array `imports` del componente standalone — Angular supporta la mescolanza di import standalone e basati su moduli. **Provider mancanti dopo la rimozione di AppModule.** I service precedentemente forniti nell'array `providers` del modulo root devono spostarsi nell'`ApplicationConfig` in `app.config.ts` o usare `providedIn: 'root'` nel decoratore `@Injectable`. I service con scope di rotta dovrebbero usare l'array `providers` nelle configurazioni di rotta. ## Conclusione - Lo schematic ufficiale dell'Angular CLI automatizza l'80-90 % della migrazione in tre passaggi sequenziali: convertire le dichiarazioni, rimuovere i moduli, cambiare il bootstrap - I moduli di routing richiedono una conversione manuale da `loadChildren` con NgModule a `loadComponent` o file di rotte standalone - Gli SharedModule sono l'ostacolo principale: convertire ogni dichiarazione condivisa in standalone individualmente, poi eliminare il modulo - Abilitare `strictStandalone` nel tsconfig per impedire l'introduzione di nuovi NgModule dopo la migrazione - Le dimensioni del bundle calano del 30-55 % grazie al tree-shaking a livello di componente e al lazy loading granulare con `loadComponent` - Gli unit test si semplificano drasticamente: importare il componente standalone direttamente in `TestBed` senza configurazione di moduli - La change detection zoneless di Angular 21 e la [reattività basata sui signal](/technologies/angular/interview-questions/angular-signals) si combinano naturalmente con l'architettura standalone per la massima performance --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/angular/angular-standalone-components-migration-best-practices