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.

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.
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.
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('');
}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.
# Step 1: Convert all declarations to standalone
ng g @angular/core:standalone --path=src/appAlla 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.
# Step 2: Remove empty NgModules
ng g @angular/core:standalone --path=src/appSelezionare "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.
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));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:
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:
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)
}
];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.
Pronto a superare i tuoi colloqui su Angular?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
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ù.
// 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 {}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.
{
"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.
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);
});
});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.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
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
loadChildrencon NgModule aloadComponento file di rotte standalone - Gli SharedModule sono l'ostacolo principale: convertire ogni dichiarazione condivisa in standalone individualmente, poi eliminare il modulo
- Abilitare
strictStandalonenel 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
TestBedsenza configurazione di moduli - La change detection zoneless di Angular 21 e la reattività basata sui signal si combinano naturalmente con l'architettura standalone per la massima performance
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Tag
Condividi
Articoli correlati

Angular 19 Zoneless: Performance e Change Detection senza Zone.js
Guida tecnica approfondita alla Zoneless Change Detection in Angular 19 e 20. Funzionamento della reattività basata su Signals, attivazione di provideZonelessChangeDetection, insidie nella migrazione con setTimeout e Reactive Forms, SSR senza Zone.js e benchmark prestazionali.

Top 25 Domande di Colloquio Angular: Guida Completa al Successo
Le 25 domande di colloquio Angular più frequenti nel 2026. Risposte dettagliate, esempi di codice e consigli per ottenere il ruolo di sviluppatore Angular.

Angular 18: Signals e nuove funzionalita
Angular 18 Signals, change detection zoneless e le nuove API basate su signal per applicazioni piu performanti.