Next.js

1. Introdução e História do Next.js

A Evolução do Render no React

  • Primórdios (Pages Router): O Next.js revolucionou o ecossistema React ao introduzir um sistema de roteamento baseado em arquivos (pages/) e renderização híbrida (SSR e SSG) de forma simples.
  • A Grande Mudança (Next.js 13 - Out/2022): Foi introduzido o App Router, uma arquitetura completamente nova baseada em React Server Components (RSCs), mudando o paradigma de "client-side por padrão" para "server-side por padrão".
  • Amadurecimento (Next.js 14 e 15): O App Router se tornou estável e recomendado. As Server Actions foram estabilizadas, simplificando drasticamente as mutações de dados.
  • O Futuro (Next.js 16): Foco em otimizações de performance e experiência do desenvolvedor, com a estabilização do Turbopack (bundler em Rust), e a introdução de conceitos avançados como Cache Components para refinar ainda mais as estratégias de cache.

2. O App Router: A Nova Era do Roteamento

O App Router, localizado na pasta app/, é a forma recomendada de criar aplicações em Next.js. Ele é mais flexível e poderoso que o antigo pages router.

Estrutura e Convenções

A navegação é definida pela estrutura de pastas. Arquivos especiais definem a UI para cada segmento de rota.

  • layout.tsx: UI compartilhada e aninhada.
  • page.tsx: A UI principal e única de uma rota.
  • loading.tsx: UI de carregamento instantâneo via Suspense.
  • error.tsx: UI para tratamento de erros.
  • not-found.tsx: UI para rotas não encontradas.

Exemplo da Estrutura de Rotas


app/
  ├── layout.tsx        // Layout raiz (\<html\> e \<body\>)
  ├── page.tsx          // Página inicial (/)
  └── dashboard/
      ├── layout.tsx    // Layout para /dashboard e suas sub-rotas
      ├── page.tsx      // Página /dashboard
      └── settings/
└── page.tsx  // Página /dashboard/settings

O componente <Link href="..."> otimiza a navegação, pré-carregando o conteúdo das páginas em segundo plano para transições instantâneas.

import Link from 'next/link';

export default function HomePage() {
  return (
    <nav>
      <Link href="/dashboard">Ir para o Dashboard</Link>
    </nav>
  );
}

Rotas Dinâmicas

Para criar rotas com segmentos que mudam (ex: blog/post-1), use colchetes [] no nome da pasta.

Estrutura do arquivo: app/blog/[slug]/page.tsx

O parâmetro slug é passado como props para o componente da página.

// app/blog/[slug]/page.tsx

type BlogPostParams = {
  params: {
    slug: string;
  };
};

export default function BlogPostPage({ params }: BlogPostParams) {
  // Você pode usar o `params.slug` para buscar dados específicos do post
  return <h1>Exibindo o post: {params.slug}</h1>;
}

3. Consumindo APIs: Padrões Modernos

Com o App Router, os componentes são Server Components por padrão. Isso significa que eles rodam no servidor e podem buscar dados diretamente, de forma segura e performática.

Data Fetching com fetch

O Next.js estende a API fetch nativa para fornecer controle granular sobre o cache.

// app/posts/page.tsx

async function getPosts() {
  // Por padrão, o resultado é cacheado de forma agressiva (static)
  const res = await fetch('[https://api.example.com/posts](https://api.example.com/posts)');
  if (!res.ok) throw new Error('Falha ao buscar posts');
  return res.json();
}

export default async function PostsPage() {
  const posts = await getPosts();

  return (
    <ul>
      {posts.map((post: any) => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

Controle de Cache e Revalidação

Você pode customizar o comportamento do cache por requisição.

1. Dados Dinâmicos (sem cache):
Para dados que mudam a cada requisição (ex: dados do usuário logado).

fetch('[https://api.example.com/data](https://api.example.com/data)', { cache: 'no-store' });

2. Revalidação por Tempo (ISR - Incremental Static Regeneration):
Para dados que podem ser atualizados periodicamente.

// Revalida (e busca novamente) a cada 60 segundos
fetch('[https://api.example.com/data](https://api.example.com/data)', { next: { revalidate: 60 } });

4. Criando seus próprios Endpoints: Route Handlers

Quando você precisa expor uma API (para um app mobile ou um webhook), usamos os Route Handlers. Eles são definidos em um arquivo route.ts ou route.js.

Estrutura: app/api/hello/route.ts

Exemplo de um Endpoint GET:

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  // Lógica de servidor aqui...
  return NextResponse.json({ message: 'Olá do meu endpoint!' });
}

// Você pode exportar funções para outros métodos: POST, PUT, DELETE, etc.
export async function POST(request: Request) {
  const data = await request.json();
  return NextResponse.json({ received: data });
}

O cliente pode então fazer uma chamada fetch para /api/hello.

5. Mutações de Dados: Server Actions

Server Actions são a forma mais moderna e integrada de lidar com mutações de dados (ex: envio de formulários). Elas permitem que o cliente chame funções que rodam diretamente no servidor, sem a necessidade de criar um endpoint de API manualmente.

Como funciona?

  1. Crie uma função assíncrona com a diretiva "use server";.
  2. Passe essa função diretamente para a prop action de um <form> ou use-a em um evento.

Exemplo de Server Action

1. Defina a ação:

// app/actions.ts
'use server';

import { revalidatePath } from 'next/cache';

// Em um cenário real, isso viria de um banco de dados
const todos: string[] = ['Aprender Next.js'];

export async function addTodo(data: FormData) {
  const todo = data.get('todo') as string;
  todos.push(todo);
  revalidatePath('/todos'); // Invalida o cache e atualiza a UI
}

2. Use-a no seu componente:

// app/todos/page.tsx
import { addTodo } from '../actions';

const todos: string[] = ['Aprender Next.js']; // Simulação de busca de dados

export default function TodosPage() {
  return (
    <div>
      <form action={addTodo}>
        <input type="text" name="todo" />
        <button type="submit">Adicionar</button>
      </form>
      <ul>{todos.map((todo, i) => <li key={i}>{todo}</li>)}</ul>
    </div>
  );
}

6. Conclusão e Próximos Passos

Recapitulação

  • Roteamento: O App Router (app/) é o padrão, com roteamento baseado em arquivos e componentes especiais (page, layout).
  • Busca de Dados (Leitura): Use async/await com fetch diretamente em Server Components. Controle o cache com as opções { cache: 'no-store' } ou { next: { revalidate: ... } }.
  • Criação de APIs: Utilize Route Handlers (route.ts) para criar endpoints de API para clientes externos.
  • Mutações de Dados (Escrita): Adote Server Actions para uma maneira segura, simples e poderosa de modificar dados a partir da sua UI.

Pratique! A melhor forma de aprender é construindo.

Obrigado!