Componentes
No Angular, componentes são classes TypeScript com decorators. No Next.js, são funções TypeScript que retornam JSX. A mudança é de paradigma, mas o conceito de componentização é o mesmo.
Estrutura básica de um componente
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
<button (click)="onSelect.emit(user)">
Selecionar
</button>
</div>
`,
})
export class UserCardComponent {
@Input() user!: { name: string; email: string };
@Output() onSelect = new EventEmitter<typeof this.user>();
}type User = { name: string; email: string };
type Props = {
user: User;
onSelect: (user: User) => void;
};
export default function UserCard({ user, onSelect }: Props) {
return (
<div className="card">
<h2>{user.name}</h2>
<p>{user.email}</p>
<button onClick={() => onSelect(user)}>
Selecionar
</button>
</div>
);
}As diferenças chave: sem @Component decorator, sem @Input() e@Output() — apenas props tipadas como parâmetro da função. Eventos são passados como callbacks tipados, não como EventEmitter.
Sintaxe de template: Angular vs. JSX
Equivalências de template
<!-- Interpolação -->
<h1>{{ title }}</h1>
<!-- Condicional -->
<p *ngIf="isLoggedIn">Bem-vindo!</p>
<p *ngIf="!isLoggedIn">Faça login.</p>
<!-- Lista -->
<ul>
<li *ngFor="let item of items; trackBy: trackById">
{{ item.name }}
</li>
</ul>
<!-- Binding de atributo -->
<img [src]="user.avatarUrl" [alt]="user.name" />
<!-- Event binding -->
<button (click)="handleClick($event)">Clique</button>
<!-- Two-way binding -->
<input [(ngModel)]="searchQuery" />{/* Interpolação */}
<h1>{title}</h1>
{/* Condicional */}
{isLoggedIn ? <p>Bem-vindo!</p> : <p>Faça login.</p>}
{isLoggedIn && <p>Bem-vindo!</p>}
{/* Lista */}
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
{/* Binding de atributo */}
<img src={user.avatarUrl} alt={user.name} />
{/* Event binding */}
<button onClick={handleClick}>Clique</button>
{/* Two-way binding (controlled input) */}
<input value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} />| Angular template | JSX (Next.js) |
|---|---|
| {{ value }} | {value} |
| *ngIf="cond" | {cond && <el>} ou ternário |
| *ngFor="let i of list" | {list.map(i => <el key={i.id} />)} |
| (click)="fn()" | onClick={fn} |
| [class]="expr" | className={expr} |
| [style.color]="color" | style={{ color }} |
| [(ngModel)]="val" | value={val} onChange={e => setVal(e.target.value)} |
| [attr]="value" | attr={value} |
| {{ value | uppercase }} | {value.toUpperCase()} |
| <!-- comentário --> | {/* comentário */} |
Projeção de conteúdo: ng-content vs. children
O equivalente de <ng-content> no React é a prop children. Para slots nomeados (como select), você usa props com tipo React.ReactNode.
// Componente que aceita conteúdo externo
@Component({
selector: 'app-card',
template: `
<div class="card">
<div class="card-header">
<ng-content select="[header]"></ng-content>
</div>
<div class="card-body">
<ng-content></ng-content>
</div>
</div>
`,
})
export class CardComponent {}
// Uso
<app-card>
<h2 header>Título do Card</h2>
<p>Conteúdo do card aqui.</p>
</app-card>// Componente que aceita children e slots nomeados
type CardProps = {
header: React.ReactNode; // slot nomeado
children: React.ReactNode; // slot padrão
};
export default function Card({ header, children }: CardProps) {
return (
<div className="card">
<div className="card-header">{header}</div>
<div className="card-body">{children}</div>
</div>
);
}
// Uso
<Card header={<h2>Título do Card</h2>}>
<p>Conteúdo do card aqui.</p>
</Card>Server Components vs. Client Components
Esta é a maior novidade do Next.js em relação ao Angular. Não existe esse conceito no Angular — todos os componentes Angular rodam no browser. No Next.js você tem dois tipos:
Server Component (padrão)
✓ async/await direto
✓ Acessa banco de dados
✓ Zero JS enviado ao cliente
✗ Sem useState, useEffect
✗ Sem event handlers
Client Component ('use client')
✓ useState, useEffect, hooks
✓ onClick, onChange, eventos
✓ Acesso ao DOM / window
✗ Sem async na raiz
✗ Sem acesso direto ao banco
// Server Component (padrão) — roda no servidor, sem JS no cliente
// Pode: usar async/await, acessar banco de dados, ler arquivos
// Não pode: useState, useEffect, onClick, eventos de browser
async function getPosts() {
return await db.post.findMany(); // acesso direto ao banco!
}
export default async function BlogPage() {
const posts = await getPosts();
return (
<ul>
{posts.map((p) => <li key={p.id}>{p.title}</li>)}
</ul>
);
}"use client"; // essa diretiva torna o componente client-side
import { useState } from "react";
// Client Component — roda no browser
// Pode: useState, useEffect, onClick, acesso ao DOM
// Não pode: async direto, acesso a banco de dados
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Cliques: {count}
</button>
);
}'use client' somente quando precisar de interatividade (estado, eventos, hooks). No Angular, pense como se todos os componentes fossem Client Components. O Server Component é um conceito novo e poderoso.Referência oficial: Server and Client Components — Next.js Docs ↗