Angular Angular Angular20 standalone components lazy loading SSR TypeScript Signals front-end

Angular 20 standalone: o que muda de verdade quando você aposenta os NgModules

Angular 20 vem mantendo sua revolução com componentes standalone, permitindo aposentar de vez os NgModules. Vamos entender o que muda na prática em formulários, lazy loading, providers e SSR, com ganhos reais em clareza, bundle e desenvolvimento.

5 min de leitura
Angular 20 standalone: o que muda de verdade quando você aposenta os NgModules

Começando do básico: você aprendeu Angular na época do app.module.ts, onde tudo dependia de NgModules: declaração de componentes, registro de services, rotas, enfim, um modelo quase burocrático. Agora, em Angular 20, os standalone components dominam o cenário (existentes desde o Angular 15) e prometem simplificar bastante, mas isso também exige uma mudança mental importante.

Antes: formulário tradicional com NgModules

Imagine um componente de formulário de cadastro simples. No paradigma clássico, teríamos algo assim:

// cadastro-form.component.ts
@Component({
  selector: 'app-cadastro-form',
  templateUrl: './cadastro-form.component.html'
})
export class CadastroFormComponent {
  nome = '';
  email = '';

  salvar() {
    console.log('Formulário salvo:', this.nome, this.email);
  }
}

E este componente seria declarado em um módulo:

@NgModule({
  declarations: [CadastroFormComponent],
  imports: [CommonModule, FormsModule],
  exports: [CadastroFormComponent]
})
export class CadastroModule {}

Para usar esse form em alguma rota, seria comum configurar lazy loading com o módulo inteiro:

const routes: Routes = [
  {
    path: 'cadastro',
    loadChildren: () =>
      import('./cadastro/cadastro.module').then(m => m.CadastroModule),
  },
];

O problema? Você precisava pedir licença ao Angular para que ele organizasse esse quebra-cabeça de NgModules, e a árvore de dependências ficava espalhada — módulos declarando componentes, módulos importando outros, providers escondidos longe dos componentes que os usam.


Depois: migrando para standalone e signals em Angular 20

Em Angular 20, o padrão é criar componentes standalone, que entendem por si só suas dependências e facilitam o lazy loading independente de NgModules.

Aqui está o mesmo formulário, agora standalone, e usando signal para reatividade:

import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-cadastro-form',
  standalone: true,
  imports: [CommonModule, FormsModule],
  template: `
    <form (ngSubmit)="salvar()">
      <label>
        Nome:
        <input [(ngModel)]="nome()"/>
      </label>
      <label>
        Email:
        <input [(ngModel)]="email()"/>
      </label>
      <button type="submit">Salvar</button>
    </form>
  `
})
export class CadastroFormComponent {
  nome = signal('');
  email = signal('');

  salvar() {
    console.log('Formulário salvo:', this.nome(), this.email());
  }
}

E para a rota com lazy loading, nada de loadChildren com módulos. Basta importar o componente standalone diretamente:

import { Routes } from '@angular/router';
import { CadastroFormComponent } from './cadastro-form.component';

export const routes: Routes = [
  {
    path: 'cadastro',
    loadComponent: () =>
      import('./cadastro-form.component').then(c => c.CadastroFormComponent)
  }
];

Aqui, percebemos dois ganhos práticos:

  • Bundle menor: sem precisar arrastar módulos inteiros, seu carregamento fica mais granular.
  • Clareza total: o componente entende o que importa, sem dependências ocultas em NgModules.

Providers e injeção: o que mudou?

Um ponto que quase ninguém avisa quando você migra para standalone é a mudança na forma de injetar serviços e declarar providers.

Onde os providers vivem?

Antes, providers eram geralmente declarados em módulos ou no @Injectable({providedIn: 'root'}). Como os módulos deixaram de ser obrigatórios, o providers dentro de componentes standalone passou a ser a forma oficial e principal de declarar provedores locais em níveis hierárquicos e sub-árvores da aplicação, sem a necessidade de um arquivo de módulo para envelopá-los:

@Component({
  standalone: true,
  providers: [MeuServico],
  // ...
})
export class MeuComponente {}

Isso delimita o escopo do service ao componente e seus descendentes, muitas vezes uma vantagem para evitar singletons desnecessários.

Usando inject() ao invés de construtor

Angular 20 encoraja o uso da função inject() para injeção:

import { inject } from '@angular/core';

@Component({
  standalone: true,
  providers: [MeuServico],
  // ...
})
export class MeuComponente {
  private meuServico = inject(MeuServico);

  // ...
}

Vantagem: não precisa declarar o service no construtor, o que deixa o código mais enxuto e facilita utilização em variáveis ou propriedades fora do contexto de classe (como em signals ou funções standalone).


Server-side rendering (SSR): o que muda?

Se você trabalha com SSR, é importante notar que Angular 20 com standalone mantém compatibilidade, mas exige alguns cuidados.

Rotas e SSR

Quando usar loadComponent para lazy loading de componentes standalone, o Angular Universal saberá resolver sem o antigo esquema do loadChildren. O SSR continua, porém o comportamento padrão de pré-renderização e revalidação se mantém.

Providers Scoped no SSR

Se você move providers para o nível do componente, tenha em mente que o ciclo de vida deles nos requests SSR passa a ser mais curto. Para serviços que precisam durar toda a requisição (ex.: contextos de usuário), opte por providers no nível do root (como providedIn: 'root').


Armadilhas comuns e pontos a observar

  • Não é só decoração: migrar para standalone não significa só mudar a flag, você deve garantir importar todos dependências explícitas em imports do componente.
  • Tratamento de rotas complexas: para rotas filhas e módulos grandes, às vezes usar módulos ainda é vantajoso pela organização.
  • SSR e efeitos colaterais: evite inicialização com efeitos colaterais no construtor, prefira inject() ou ngOnInit.
  • Signals não são magia: prefira signals para estados locais simples, mas gerenciadores mais robustos ainda podem precisar de RxJS em apps complexos.

Resumo dos ganhos e desafios

Aspecto Angular com NgModules Angular 20 standalone
Estrutura Muita burocracia com módulos Componentes autocontidos
Lazy loading Modulos pesados Lazy por componente (loadComponent)
Declaração de providers No módulo ou root Em componente com providers ou root
Injeção de dependências Construtor Função inject()
Bundle e carregamento Maior Mais enxuto e granular
SSR Compatível Requer atenção a escopo de providers

Próximos passos

Se você está voltando para Angular ou iniciando migração para standalone:

  • Experimente converter um módulo pequeno para standalone e meça impacto no bundle.
  • Utilize signals para estado simples e observe a melhora na performance.
  • No SSR, teste cuidadosamente providers escopados.
  • Acompanhe a documentação oficial do Angular 17 para detalhes práticos.

Angular 20 não é apenas uma evolução técnica, mas um convite para pensarmos em aplicações cada vez mais claras, modulares e performáticas. Adaptar a mentalidade de NgModules para componentes standalone vai trazer muito ganho, basta explorar com calma e paciência.

Blog do Felipe Marciano

Decisões de arquitetura, .NET e sistemas distribuídos, escritas por quem vive com as consequências delas.

© 2026 Felipe Marciano. Todos os direitos reservados.