← Artigos

Angular e Next.js,o que muda entre os dois.

Trabalho com Angular em sistemas internos e com Next.js em produtos e páginas públicas. Aqui comparo os dois na prática, com código: componentes, onde o código roda, dados, reatividade, rotas e formulários. No fim, digo o que eu recomendo para cada caso.

14 de abril de 2026 · 6 min de leitura

AngularNext.jsReactFront-end
Artigo

Passo a semana alternando entre os dois. Sistemas administrativos, com muita tela de cadastro, tabela com filtro e formulário, saem em Angular. Produtos que precisam de SEO, páginas públicas e este site saem em Next.js. Os dois resolvem o mesmo problema, montar interface com TypeScript, mas partem de premissas diferentes. O que mais custa na troca é levar a premissa errada de um para o outro.

1. Um framework completo e um framework sobre o React

O Angular vem com quase tudo dentro: componentes, injeção de dependência, roteador, formulários, cliente HTTP, ferramentas de teste e uma CLI que gera e atualiza o projeto [1]. O Next.js é um framework em cima do React. Ele traz roteamento, renderização no servidor, cache e Server Functions, e deixa o resto para você escolher [2].

AngularVEM NO FRAMEWORKComponentesInjeção de dependênciaRoteadorFormuláriosCliente HTTPTestesCLISSR (opcional)Next.jsVEM NO FRAMEWORKRotas por pastasServer ComponentsServer FunctionsCacheReact — componentes e hooksVOCÊ ESCOLHEEstado globalFormuláriosCliente HTTPTestesUM PROJETO ANGULARse parece com o outro.EM NEXT.JSas convenções do time precisam estar escritas.
Fig. 001 — O que cada um traz pronto. No Next.js, a parte tracejada é decisão do time.

Na prática, um projeto Angular novo se parece com qualquer outro projeto Angular. Em Next.js, dois projetos podem ter escolhas completamente diferentes de estado, formulário e busca de dados. Isso dá liberdade, mas cobra combinado: no Next, as convenções do time precisam estar escritas, porque o framework não as impõe.

2. Componentização

Os dois são orientados a componentes, mas o que é um componente muda bastante. No Angular, um componente é uma classe com um decorador, um template HTML e estilos próprios. Ele recebe dados por input() [12], avisa o pai por output() [13] e recebe conteúdo de fora pelo <ng-content> [14]. Os estilos ficam presos ao componente por padrão: o CSS dele não vaza para o resto da página [15].

card.ts
@Component({
  selector: 'app-card',
  templateUrl: './card.html',
  styleUrl: './card.css',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Card {
  readonly titulo = input.required<string>()
  readonly destaque = input(false)
  readonly fechar = output<void>()
}
card.html
<article class="card" [class.destaque]="destaque()">
  <header>
    <h2>{{ titulo() }}</h2>
    <button type="button" (click)="fechar.emit()">Fechar</button>
  </header>
  <ng-content />
</article>
uso no template do pai
<app-card titulo="Resumo" [destaque]="true" (fechar)="ocultar()">
  <p>Este conteúdo é projetado dentro do card.</p>
</app-card>

No React, um componente é uma função que recebe props e devolve JSX. Não tem decorador nem arquivo de template separado, evento é só uma função passada como prop, e o conteúdo de dentro chega pela prop children [16]:

card.tsx
import type { ReactNode } from 'react'

type CardProps = {
  titulo: string
  destaque?: boolean
  onFechar: () => void
  children: ReactNode
}

export function Card({ titulo, destaque = false, onFechar, children }: CardProps) {
  return (
    <article className={destaque ? 'card card--destaque' : 'card'}>
      <header>
        <h2>{titulo}</h2>
        <button type="button" onClick={onFechar}>Fechar</button>
      </header>
      {children}
    </article>
  )
}
uso no componente pai
<Card titulo="Resumo" destaque onFechar={ocultar}>
  <p>Este conteúdo chega como children.</p>
</Card>

A diferença de verdade aparece no resto. No Angular, cada tipo de reuso tem o seu bloco: componente para interface, diretiva para comportamento num elemento, pipe para formatar valor e serviço para lógica e estado compartilhado. No React, é tudo função: lógica reaproveitável vira um hook, variação de interface vira prop ou composição, e o estilo é escolha sua, seja CSS Modules, Tailwind ou CSS puro.

Por isso o React, e o Next.js em cima dele, é bem mais customizável. Dá para montar a arquitetura do jeito que o projeto pede, com as bibliotecas que o time preferir. O Angular troca essa liberdade por padrão: menos decisões para tomar e um projeto que todo mundo do time lê do mesmo jeito. Nenhum dos dois é melhor por isso. São apostas diferentes.

3. Onde o código roda

Esta é a maior diferença, e a que mais confunde. Uma aplicação Angular é, por padrão, uma SPA: o navegador baixa o JavaScript e tudo roda nele. O SSR existe e funciona bem, mas é uma escolha que você adiciona com ng add @angular/ssr [3].

No App Router do Next.js é o contrário: páginas e layouts são Server Components por padrão [2]. Eles rodam no servidor, podem acessar banco e segredos e não mandam o próprio código para o navegador. Só vira código de navegador o que você marca com 'use client' [4].

SERVIDORNAVEGADORAngularAPI (back-end)JAVA · SPRINGSSR (opcional)A aplicação inteiraComponentesServiçosRoteadorbaixa o JavaScript e roda no navegadorHTTP · JSONNext.js — App Router'use client'Banco, API, segredosNUNCA SAEM DAQUIpage.tsxSERVER COMPONENTBotaoFavoritarCLIENT COMPONENTawaitpropsrenderiza no servidor, não manda o próprio códigoestado e eventos: vai para o navegador
Fig. 002 — Onde o código roda. No Next.js, a fronteira entre servidor e navegador é marcada no código.
app/produtos/[id]/page.tsx
import { BotaoFavoritar } from './botao-favoritar'
import { buscarProduto } from '@/lib/produtos'

// Server Component: roda no servidor e pode ser async.
export default async function Page({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const produto = await buscarProduto(id)

  return (
    <main>
      <h1>{produto.nome}</h1>
      <BotaoFavoritar inicial={produto.favorito} />
    </main>
  )
}
app/produtos/[id]/botao-favoritar.tsx
'use client'

import { useState } from 'react'

// Client Component: tem estado e evento, então vai para o navegador.
export function BotaoFavoritar({ inicial }: { inicial: boolean }) {
  const [favorito, setFavorito] = useState(inicial)

  return (
    <button onClick={() => setFavorito(!favorito)}>
      {favorito ? 'Favoritado' : 'Favoritar'}
    </button>
  )
}

A regra que eu sigo: começo tudo como Server Component e só levo para o cliente o pedaço que precisa de estado, evento ou API do navegador. Quanto menor a ilha de 'use client', menos JavaScript a página manda [2]. Um detalhe que pega quem vem de versões antigas: desde o Next.js 15, params é uma Promise e precisa de await.

4. Buscar dados

No Angular, a busca de dados mora num serviço injetável, com o HttpClient devolvendo um Observable. O componente pede o serviço com inject() e transforma o resultado em signal para usar no template:

produtos.api.ts
@Injectable({ providedIn: 'root' })
export class ProdutosApi {
  private readonly http = inject(HttpClient)

  listar(): Observable<Produto[]> {
    return this.http.get<Produto[]>('/api/produtos')
  }
}
produtos.ts
@Component({
  selector: 'app-produtos',
  templateUrl: './produtos.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProdutosPage {
  private readonly api = inject(ProdutosApi)

  protected readonly produtos = toSignal(this.api.listar(), { initialValue: [] })
  protected readonly busca = signal('')
  protected readonly filtrados = computed(() =>
    this.produtos().filter((p) =>
      p.nome.toLowerCase().includes(this.busca().toLowerCase()),
    ),
  )
}

No Next.js, num Server Component, buscar dado é só await. Não tem useEffect nem estado de carregamento escrito à mão: o componente é uma função assíncrona e o Next cuida do streaming [5]. Separar a busca numa camada própria continua sendo boa prática, mas é uma decisão sua, não do framework.

app/produtos/page.tsx
export default async function Page() {
  const produtos = await buscarProdutos()

  return (
    <ul>
      {produtos.map((p) => (
        <li key={p.id}>{p.nome}</li>
      ))}
    </ul>
  )
}

5. Reatividade: signals e re-render

O Angular moderno é construído em cima de signals. Um signal é um valor que avisa quem depende dele quando muda, e um computed deriva de outros signals: só calcula quando é lido e guarda o resultado até uma dependência mudar [6]. No Angular 21, projetos novos já nem incluem o zone.js; a atualização da tela é guiada pelos signals [7].

No React, a unidade é o componente: quando o estado muda, a função do componente roda de novo e o React compara o resultado. O mesmo filtro do exemplo acima fica assim:

lista-produtos.tsx
'use client'

import { useMemo, useState } from 'react'

export function ListaProdutos({ produtos }: { produtos: Produto[] }) {
  const [busca, setBusca] = useState('')
  const filtrados = useMemo(
    () =>
      produtos.filter((p) =>
        p.nome.toLowerCase().includes(busca.toLowerCase()),
      ),
    [produtos, busca],
  )
  // ...
}

O que muda na cabeça: no Angular eu penso em quais valores dependem de quais; no React, em quando o componente renderiza e no que precisa ser memorizado. Esquecer uma dependência no array do useMemo é o erro clássico de quem vem do Angular. Ler um signal fora de um contexto reativo e esperar que ele se atualize é o erro clássico de quem faz o caminho inverso.

6. Rotas

No Angular, as rotas são um array de configuração, com guard e lazy loading declarados por rota [8]:

app.routes.ts
export const routes: Routes = [
  {
    path: 'produtos',
    canActivate: [autenticadoGuard],
    loadChildren: () =>
      import('./produtos/produtos.routes').then((m) => m.PRODUTOS_ROUTES),
  },
  {
    path: '**',
    loadComponent: () =>
      import('./erro/nao-encontrada').then((m) => m.NaoEncontradaPage),
  },
]

No Next.js, a rota é a pasta. app/produtos/[id]/page.tsx já é a rota /produtos/:id, e arquivos com nome reservado, como layout.tsx e loading.tsx, cuidam da moldura e do estado de carregamento:

estrutura de pastas
app/
├── layout.tsx              → moldura de todas as páginas
├── page.tsx                → /
└── produtos/
    ├── page.tsx            → /produtos
    └── [id]/
        ├── page.tsx        → /produtos/:id
        └── loading.tsx     → estado de carregando da rota

Não existe um equivalente direto do guard. A checagem de acesso acontece no servidor, na página, no layout ou na Server Function, e redirecionamentos gerais ficam no proxy, que é o antigo middleware renomeado no Next.js 16 [9].

7. Formulários

Formulário é onde o Angular mais brilha nos sistemas que eu faço. Os Reactive Forms vêm no framework, com validação, estado de campo tocado e alterado, e tipagem [10]:

novo-produto.ts
export class NovoProdutoPage {
  private readonly fb = inject(NonNullableFormBuilder)

  protected readonly form = this.fb.group({
    nome: ['', [Validators.required, Validators.maxLength(120)]],
    preco: [0, [Validators.required, Validators.min(0)]],
  })
}

No Next.js, o caminho nativo é o formulário HTML chamando uma Server Function pelo action. O dado vai direto para o servidor e chega como FormData [11]. Para validação mais rica no cliente, entra uma biblioteca de sua escolha.

app/produtos/actions.ts
'use server'

export async function criarProduto(formData: FormData) {
  // Toda Server Function é um endpoint público:
  // valide e cheque permissão aqui, mesmo que o formulário já valide.
  const nome = String(formData.get('nome') ?? '').trim()
  if (!nome) throw new Error('Nome é obrigatório')

  // salvar, revalidar o cache…
}
app/produtos/novo/page.tsx
<form action={criarProduto}>
  <input name="nome" required />
  <button type="submit">Salvar</button>
</form>

O que eu recomendo

Esta parte é opinião minha, depois de usar os dois em produção:

  • Para sistema interno grande, eu recomendo Angular. Muitas telas de cadastro, muito formulário, time grande ou rotativo: a estrutura imposta faz um projeto se parecer com o outro, e quem entra no time acha as coisas onde espera. Só os Reactive Forms já pagam a escolha.
  • Para produto, página pública e tudo que depende de SEO, eu recomendo Next.js. Renderizar no servidor por padrão deixa a página rápida sem esforço extra, e a liberdade do React permite montar exatamente a interface que o produto pede.
  • Se o time quer flexibilidade, Next.js; se precisa de padrão, Angular. A customização do React é ótima quando alguém cuida das convenções. Sem isso, cada parte do projeto acaba com um estilo diferente.
  • Na dúvida, escolha o que o time já domina. Os dois atendem bem a maioria dos casos, e a curva de aprendizado custa mais do que qualquer diferença de recurso.

O que levo de um para o outro

Do Angular para o Next, levo a separação da busca de dados em funções próprias, mesmo sem o framework exigir, e a tipagem forte nas bordas. Do Next para o Angular, levo a pergunta "isso precisa mesmo rodar no navegador?" e a disposição de ligar o SSR quando a página é pública.

E dos dois, uma tendência: signals de um lado e Server Components do outro estão empurrando os frameworks na mesma direção, com menos trabalho escondido e mais clareza sobre o que reage a quê e onde cada coisa roda. Quem entende essas duas ideias troca de um para o outro sem sofrer.

Fontes

  1. What is Angular? — Documentação do Angular
  2. Server and Client Components — Documentação do Next.js
  3. Server-side and hybrid rendering — Documentação do Angular
  4. Server Components — Documentação do React
  5. Fetching Data — Documentação do Next.js
  6. Signals — Documentação do Angular
  7. What’s new in Angular 21.0? — Ninja Squad, novembro de 2025
  8. Lazy loading routes — Documentação do Angular
  9. Proxy — Documentação do Next.js
  10. Reactive forms — Documentação do Angular
  11. Mutating Data — Documentação do Next.js
  12. Accepting data with input properties — Documentação do Angular
  13. Custom events with outputs — Documentação do Angular
  14. Content projection with ng-content — Documentação do Angular
  15. Styling components — Documentação do Angular
  16. Passing Props to a Component — Documentação do React