ngnext

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

Angularuser-card.component.ts
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>();
}
Next.jsUserCard.tsx
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

Angular
<!-- 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" />
Next.js
{/* 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 templateJSX (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.

Angularcard.component.ts
// 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>
Next.jsCard.tsx
// 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

ServerComponent.tsx
// 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>
  );
}
ClientComponent.tsx
"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>
  );
}
Regra prática: Comece com Server Components. Adicione '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.