Angular Standalone Components: Migração e Boas Práticas para Angular 22
Guia completo para migrar aplicações Angular de NgModules para componentes standalone. O schematic CLI oficial em 3 etapas, componentes sem seletor, OnPush por padrão, e lazy loading com loadComponent no Angular 22.

Os componentes standalone do Angular eliminam a necessidade de NgModules, reduzindo boilerplate e habilitando lazy loading granular em toda a aplicação. Desde que o Angular 19 tornou standalone o padrão e o Angular 22 introduziu componentes sem seletor (selectorless) como estáveis, a migração de bases de código legadas baseadas em módulos se tornou simples e de alto impacto.
O schematic oficial do Angular CLI automatiza a maior parte da migração em três etapas. Uma aplicação empresarial típica pode completar a conversão em um único sprint, com reduções de tamanho de bundle de 30-50% graças ao lazy loading por componente.
NgModules vs Componentes Standalone: O que mudou
Os NgModules serviram como contexto de compilação para componentes desde o Angular 2. Cada componente, diretiva e pipe precisava ser declarado em exatamente um módulo, e funcionalidades compartilhadas exigiam importações e exportações de módulos cuidadosamente orquestradas. Isso criava acoplamento forte entre funcionalidades não relacionadas e dificultava o tree-shaking.
Os componentes standalone invertem esse modelo. Cada componente declara suas próprias dependências diretamente no array imports do decorator @Component. Sem registro de módulos, sem SharedModules, sem barrel exports de metade da aplicação. A partir do Angular 22, esse padrão é simplificado ainda mais com componentes sem seletor, permitindo importações diretas sem seletores de texto.
import { Component, signal } from '@angular/core';
import { HeroCardComponent } from './hero-card.component';
import { SearchPipe } from '../pipes/search.pipe';
@Component({
selector: 'app-hero-list',
standalone: true,
imports: [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('');
}O array imports substitui todo o grafo de dependências dos NgModules. O bundler vê exatamente quais componentes, pipes e diretivas cada arquivo precisa, habilitando tree-shaking preciso. O CommonModule não é mais necessário no Angular 22 porque a sintaxe de controle de fluxo (@for, @if) está embutida no compilador.
O processo de migração via CLI em 3 etapas
O Angular fornece um schematic automatizado que gerencia a migração em três passadas sequenciais. Cada etapa se baseia na anterior, e o projeto deve compilar corretamente entre cada passada.
Etapa 1: Converter declarações para standalone
A primeira passada analisa cada componente, diretiva e pipe do projeto, adiciona standalone: true, e move as importações necessárias do NgModule pai para o array imports de cada componente.
# Step 1: Convert all declarations to standalone
ng generate @angular/core:standalone --path=src/appSelecionar "Convert all components, directives and pipes to standalone" quando o prompt aparecer. O schematic usa análise estática para resolver dependências, qualquer componente com metadata que não possa ser analisada em tempo de build será ignorado com um aviso.
Etapa 2: Remover NgModules desnecessários
Com todas as declarações agora standalone, muitos NgModules se tornam cascas vazias. Esta passada identifica módulos que apenas reexportavam declarações standalone e os remove.
# Step 2: Remove empty NgModules
ng generate @angular/core:standalone --path=src/appSelecionar "Remove unnecessary NgModule classes". Módulos que ainda contêm providers, configurações de rotas, ou são importados por múltiplos outros módulos serão preservados com um comentário TODO para revisão manual.
Angular 22 requer TypeScript 6 e Node.js 26. Versões anteriores não são suportadas. Executar ng version para verificar compatibilidade antes de iniciar a migração.
Etapa 3: Mudar para bootstrap standalone
A passada final substitui o NgModule raiz pela API bootstrapApplication do Angular e converte o componente raiz para 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, provideZoneChangeDetection } 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: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor]))
]
};O padrão ApplicationConfig substitui os arrays providers e imports do módulo raiz. Todas as funções provider (provideRouter, provideHttpClient, provideAnimations) funcionam diretamente sem wrappers de módulos.
Angular 22: OnPush por padrão e componentes sem seletor
O Angular 22 introduziu duas mudanças que afetam o desenvolvimento de componentes standalone: OnPush como estratégia de detecção de mudanças padrão e componentes sem seletor alcançando status estável.
OnPush por padrão
Novos componentes gerados no Angular 22 usam ChangeDetectionStrategy.OnPush por padrão. Isso incentiva reatividade baseada em signals sobre verificações disparadas por Zone.js. Durante a migração, o schematic adiciona explicitamente changeDetection: ChangeDetectionStrategy.Default a componentes existentes para manter compatibilidade retroativa.
// Angular 22: OnPush is implicit for new components
@Component({
selector: 'app-dashboard',
standalone: true,
// changeDetection: ChangeDetectionStrategy.OnPush is now the default
template: \`<h1>{{ title() }}</h1>\`
})
export class DashboardComponent {
title = signal('Dashboard');
}Para aplicações migradas, a adoção de OnPush incrementalmente é feita substituindo propriedades de classe por signals. Esta abordagem se alinha com a arquitetura signal-first que o Angular 22 promove.
Componentes sem seletor (estável)
Componentes sem seletor permitem importar componentes diretamente em templates sem definir seletores de texto. Isso elimina conflitos de nomes de seletores em bases de código grandes e fornece melhor segurança de tipos.
// Traditional approach with selector
@Component({
selector: 'app-user-card',
standalone: true,
template: \`<div class="user-card">{{ user().name }}</div>\`
})
export class UserCardComponent {
user = input.required<User>();
}
// Selectorless approach (Angular 22+)
@Component({
standalone: true,
template: \`<div class="user-card">{{ user().name }}</div>\`
})
export class UserCardComponent {
user = input.required<User>();
}Em templates, componentes sem seletor são usados pelo nome da classe em vez de uma tag de seletor personalizada. O compilador garante que o import existe, detectando erros em tempo de build em vez de runtime.
Migração de rotas: de módulos para loadComponent
Módulos de roteamento requerem atenção manual porque o schematic não converte automaticamente imports de módulos loadChildren para loadComponent ou loadChildren em nível de rota com rotas standalone.
O padrão legado carregava módulos de funcionalidades inteiros:
const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashboard/dashboard.module')
.then(m => m.DashboardModule)
}
];O equivalente standalone carrega componentes individuais ou arquivos de rotas diretamente:
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 carrega um único componente com lazy loading. loadChildren com um arquivo de rotas carrega uma área de funcionalidade inteira com lazy loading. Ambos produzem chunks separados que o navegador busca sob demanda.
Pronto para mandar bem nas entrevistas de Angular?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Tratamento de SharedModules e dependências comuns
SharedModules, aqueles módulos catch-all que exportam componentes, diretivas e pipes comumente usados, são o bloqueador mais comum durante a migração. O schematic não pode removê-los automaticamente porque múltiplos módulos os importam.
A solução: converter declarações compartilhadas para standalone individualmente, depois deletar o SharedModule uma vez que nada mais o importe.
// 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 {}Consumidores agora importam LoadingSpinner diretamente em vez do SharedModule inteiro. O bundler inclui apenas os componentes específicos que cada rota precisa.
Forçando desenvolvimento exclusivamente standalone
Após a migração, prevenir que novos NgModules voltem à base de código é essencial. O Angular fornece uma opção do compilador TypeScript para isso.
{
"angularCompilerOptions": {
"strictStandalone": true
}
}Com strictStandalone habilitado, qualquer tentativa de criar um componente, diretiva ou pipe não standalone produz um erro de compilação. Isso impõe a nova arquitetura em todo o time. A migração de formulários Angular segue o mesmo padrão com Signal Forms agora estáveis no Angular 22.
Ganhos de performance: tamanho de bundle e lazy loading
O principal benefício de performance dos componentes standalone vem do lazy loading granular. Com NgModules, lazy loading operava no nível de módulo: importar um componente de um módulo trazia todas as declarações que aquele módulo exportava. Componentes standalone quebram esse acoplamento.
Um benchmark real em uma aplicação empresarial de médio porte (200+ componentes) mediu antes e depois da migração standalone:
| Métrica | Baseado em NgModule | Standalone | Melhoria |
|---|---|---|---|
| Bundle inicial | 485 KB | 218 KB | -55% |
| Maior chunk lazy | 142 KB | 38 KB | -73% |
| Time to Interactive | 3.2s | 1.8s | -44% |
| Tempo de build (esbuild) | 12.4s | 8.1s | -35% |
Esses números resultam da remoção do overhead de resolução de módulos e da habilitação do bundler para eliminar exports não usados no nível de componente em vez do nível de módulo.
Testando componentes standalone com Vitest
Testes unitários se simplificam significativamente com componentes standalone. O Angular 22 tornou o Vitest o test runner padrão, substituindo o Karma. A configuração do TestBed não mais requer importar módulos inteiros para satisfazer as dependências de um componente.
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroListComponent } from './hero-list.component';
import { HeroService } from '../services/hero.service';
import { of } from 'rxjs';
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);
});
});O componente vai diretamente no array imports do TestBed.configureTestingModule. Todas as suas dependências declaradas já estão resolvidas através de seus próprios imports, nenhuma importação de módulo adicional é necessária.
Armadilhas comuns na migração
Imports circulares entre componentes standalone. Quando o componente A importa o componente B e B importa A, o compilador TypeScript gera um erro de dependência circular. A solução: extrair a interface compartilhada para um arquivo separado ou usar forwardRef() como workaround temporário enquanto refatora a cadeia de dependências.
Bibliotecas de terceiros ainda usando NgModules. Muitas bibliotecas migraram para standalone, mas alguns pacotes legados ainda exportam NgModules. Importar esses módulos diretamente no array imports do componente standalone funciona, o Angular suporta misturar imports standalone e baseados em módulos.
Providers faltando após remover AppModule. Serviços previamente fornecidos no array providers do módulo raiz devem migrar para ApplicationConfig em app.config.ts ou usar providedIn: 'root' no decorator @Injectable. Serviços com escopo de rota devem usar o array providers em configurações de rota.
Quando perguntado sobre componentes standalone em uma entrevista Angular, focar em três pontos: (1) gerenciamento de dependências passa do nível de módulo para nível de componente, (2) tree-shaking se torna granular, e (3) configuração de testes simplifica porque componentes declaram seus próprios imports.
Fontes
- Angular v22 Release Notes - OnPush por padrão, selectorless estável, requisito TypeScript 6
- Angular Standalone Migration Guide - Documentação oficial do schematic em 3 etapas
- What's New in Angular 22 - Changelog detalhado incluindo mudanças na estratégia de detecção de mudanças
- Selectorless Components RFC - Rationale de design e status de implementação
Migrando para standalone: pontos principais para Angular 22
- O schematic oficial do CLI Angular automatiza 80-90% da migração através de três passadas sequenciais: converter declarações, remover módulos, mudar bootstrap
- Módulos de roteamento precisam de conversão manual de
loadChildrencom NgModules paraloadComponentou arquivos de rotas standalone - SharedModules são o principal bloqueador, converter cada declaração compartilhada para standalone individualmente, depois deletar o módulo
- Habilitar
strictStandaloneno tsconfig para prevenir que novos NgModules sejam introduzidos pós-migração - Tamanhos de bundle caem 30-55% graças ao tree-shaking em nível de componente e lazy loading granular com
loadComponent - Testes unitários simplificam com Vitest como runner padrão, importar o componente standalone diretamente no
TestBedsem configuração de módulo - OnPush por padrão e componentes sem seletor do Angular 22 combinam naturalmente com arquitetura standalone para uma base de código mais limpa e manutenível
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Você saberia encontrar o bug em Angular?
Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Escrito por
Anthony Fillion-MailletFundador da SharpSkill
Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.
Atualizado em 21 de agosto de 2026
Tags
Compartilhar
Artigos relacionados

Angular 20 em 2026: Resource API, httpResource e perguntas de entrevista
O Angular 20 introduz o httpResource e estabiliza a Resource API para a busca de dados baseada em signals. Um tutorial prático sobre resource(), rxResource(), httpResource(), a validação com Zod e as perguntas de entrevista mais comuns.

Angular @defer em 2026: Lazy Loading Declarativo e Entrevistas
Domine os blocos @defer do Angular para lazy loading declarativo no nível de componente. Guia completo sobre gatilhos, prefetching, hidratação incremental, comportamento SSR e padrões de performance para aplicações reais.

Injeção de Dependências Avançada no Angular 2026: Providers, Tokens e Perguntas de Entrevista
Dominar a injeção de dependências no Angular com InjectionToken, injetores hierárquicos, modificadores de resolução e multi-providers. Inclui perguntas de entrevista e padrões de produção.