Next.js 15: A Nova Era do Desenvolvimento Web
O Next.js 15 representa um salto significativo no ecossistema React, trazendo melhorias de performance, novas APIs e recursos que simplificam o desenvolvimento de aplicações web modernas. Neste guia completo, vamos explorar todas as novidades e como aplicá-las em projetos reais.
O que há de novo no Next.js 15?
O Next.js 15 traz diversas melhorias significativas:
Principais Novidades
| Feature | Descrição | Impacto |
|---|---|---|
| Turbopack Stable | Bundler ultra-rápido | 10x mais rápido |
| React 19 Support | Novas features do React | Actions, Compiler |
| Async Request APIs | await cookies/headers | Breaking change |
| Partial Prerendering | Static + Dynamic | Performance |
| Enhanced Caching | Novo modelo de cache | Mais controle |
"Next.js 15 não é apenas uma atualização, é uma reimaginação de como construímos aplicações web." - Guillermo Rauch, CEO Vercel
Instalação e Migração
Novo Projeto
# Criar novo projeto Next.js 15
npx create-next-app@latest my-app --typescript --tailwind --eslint
# Opções recomendadas:
# ✔ Would you like to use App Router? Yes
# ✔ Would you like to customize the default import alias? Yes (@/*)
Migração de Projeto Existente
# Atualizar dependências
npm install next@latest react@latest react-dom@latest
# Ou com codemod automático
npx @next/codemod@latest upgrade latest
Turbopack: O Novo Bundler
O Turbopack agora é estável para desenvolvimento e oferece performance incrível:
# Habilitar Turbopack
next dev --turbo
# Ou no package.json
{
"scripts": {
"dev": "next dev --turbo"
}
}
Benchmarks de Performance
| Métrica | Webpack | Turbopack | Melhoria |
|---|---|---|---|
| Cold Start | 8.2s | 1.1s | 7.5x |
| Fast Refresh | 500ms | 50ms | 10x |
| Build Time | 45s | 12s | 3.7x |
Configuração Avançada
// next.config.ts
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
experimental: {
turbo: {
rules: {
'*.svg': {
loaders: ['@svgr/webpack'],
as: '*.js',
},
},
},
},
};
export default nextConfig;
Async Request APIs
Uma das mudanças mais importantes: APIs de request agora são assíncronas.
Antes (Next.js 14)
// ❌ Forma antiga - síncrona
import { cookies, headers } from 'next/headers';
export default function Page() {
const cookieStore = cookies();
const headersList = headers();
return <div>...</div>;
}
Depois (Next.js 15)
// ✅ Forma nova - assíncrona
import { cookies, headers } from 'next/headers';
export default async function Page() {
const cookieStore = await cookies();
const headersList = await headers();
const theme = cookieStore.get('theme')?.value;
const userAgent = headersList.get('user-agent');
return (
<div>
<p>Theme: {theme}</p>
<p>User Agent: {userAgent}</p>
</div>
);
}
Params e SearchParams também são async
// app/blog/[slug]/page.tsx
interface Props {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}
export default async function BlogPost({ params, searchParams }: Props) {
const { slug } = await params;
const { page = '1' } = await searchParams;
const post = await getPost(slug);
return (
<article>
<h1>{post.title}</h1>
<p>Page: {page}</p>
</article>
);
}
Server Actions Aprimorados
Server Actions ficaram ainda mais poderosos no Next.js 15:
Definindo Server Actions
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
// Schema de validação
const CreatePostSchema = z.object({
title: z.string().min(3).max(100),
content: z.string().min(10),
published: z.boolean().default(false),
});
// Tipo para o estado do formulário
type FormState = {
success: boolean;
message: string;
errors?: {
title?: string[];
content?: string[];
};
};
export async function createPost(
prevState: FormState,
formData: FormData
): Promise<FormState> {
// Validar dados
const validatedFields = CreatePostSchema.safeParse({
title: formData.get('title'),
content: formData.get('content'),
published: formData.get('published') === 'on',
});
if (!validatedFields.success) {
return {
success: false,
message: 'Erro de validação',
errors: validatedFields.error.flatten().fieldErrors,
};
}
try {
// Salvar no banco
await db.post.create({
data: validatedFields.data,
});
// Revalidar cache
revalidatePath('/posts');
return {
success: true,
message: 'Post criado com sucesso!',
};
} catch (error) {
return {
success: false,
message: 'Erro ao criar post',
};
}
}
Usando com useActionState (React 19)
// app/posts/new/page.tsx
'use client';
import { useActionState } from 'react';
import { createPost } from '@/app/actions';
const initialState = {
success: false,
message: '',
};
export default function NewPostForm() {
const [state, formAction, isPending] = useActionState(
createPost,
initialState
);
return (
<form action={formAction} className="space-y-4">
<div>
<label htmlFor="title" className="block font-medium">
Título
</label>
<input
type="text"
id="title"
name="title"
className="w-full border rounded-lg p-2"
disabled={isPending}
/>
{state.errors?.title && (
<p className="text-red-500 text-sm">{state.errors.title[0]}</p>
)}
</div>
<div>
<label htmlFor="content" className="block font-medium">
Conteúdo
</label>
<textarea
id="content"
name="content"
rows={5}
className="w-full border rounded-lg p-2"
disabled={isPending}
/>
{state.errors?.content && (
<p className="text-red-500 text-sm">{state.errors.content[0]}</p>
)}
</div>
<button
type="submit"
disabled={isPending}
className="bg-blue-500 text-white px-4 py-2 rounded-lg disabled:opacity-50"
>
{isPending ? 'Criando...' : 'Criar Post'}
</button>
{state.message && (
<p className={state.success ? 'text-green-500' : 'text-red-500'}>
{state.message}
</p>
)}
</form>
);
}
Partial Prerendering (PPR)
O Partial Prerendering combina o melhor de SSG e SSR na mesma página:
Habilitando PPR
// next.config.ts
const nextConfig: NextConfig = {
experimental: {
ppr: 'incremental', // ou true para todas as páginas
},
};
Usando PPR na Página
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { DashboardSkeleton } from '@/components/skeletons';
import { DynamicStats } from '@/components/dynamic-stats';
// Habilitar PPR para esta rota
export const experimental_ppr = true;
// Parte estática - pré-renderizada no build
function StaticHeader() {
return (
<header className="bg-white shadow">
<h1 className="text-2xl font-bold">Dashboard</h1>
<nav>...</nav>
</header>
);
}
// Parte dinâmica - streamed no request
async function DynamicContent() {
const stats = await fetchRealTimeStats();
return <DynamicStats data={stats} />;
}
export default function DashboardPage() {
return (
<div>
{/* Static shell - entregue instantaneamente */}
<StaticHeader />
{/* Dynamic content - streamed depois */}
<Suspense fallback={<DashboardSkeleton />}>
<DynamicContent />
</Suspense>
</div>
);
}
Como funciona?
- Build time: Shell estático é pré-renderizado
- Request time: Shell é servido instantaneamente do edge
- Streaming: Conteúdo dinâmico é streamed assim que pronto
Novo Modelo de Caching
O Next.js 15 introduz mudanças importantes no sistema de cache:
Mudanças Principais
| API | Next.js 14 | Next.js 15 |
|---|---|---|
| fetch() | Cached por padrão | Não cached |
| GET Route Handlers | Cached | Não cached |
| Client Router | Cached 30s | Não cached |
Configurando Cache Explicitamente
// Cache explícito para fetch
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache', // Habilitar cache
next: {
revalidate: 3600, // Revalidar a cada hora
tags: ['posts'], // Tag para revalidação seletiva
},
});
// Ou sem cache
const liveData = await fetch('https://api.example.com/live', {
cache: 'no-store',
});
unstable_cache para Funções
import { unstable_cache } from 'next/cache';
const getCachedPosts = unstable_cache(
async () => {
const posts = await db.post.findMany();
return posts;
},
['posts'], // Cache key
{
revalidate: 3600, // 1 hora
tags: ['posts'],
}
);
// Revalidar por tag
import { revalidateTag } from 'next/cache';
revalidateTag('posts');
React 19 no Next.js 15
O Next.js 15 vem com suporte completo ao React 19:
useOptimistic
'use client';
import { useOptimistic } from 'react';
import { likePost } from '@/app/actions';
export function LikeButton({ postId, initialLikes }: Props) {
const [optimisticLikes, addOptimisticLike] = useOptimistic(
initialLikes,
(state, newLike: number) => state + newLike
);
async function handleLike() {
addOptimisticLike(1); // UI atualiza imediatamente
await likePost(postId); // Server action em background
}
return (
<button onClick={handleLike}>
❤️ {optimisticLikes}
</button>
);
}
use() Hook
'use client';
import { use } from 'react';
export function Comments({ commentsPromise }: { commentsPromise: Promise<Comment[]> }) {
// Suspense boundary necessário acima
const comments = use(commentsPromise);
return (
<ul>
{comments.map(comment => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
Otimizações de Performance
next/image Melhorado
import Image from 'next/image';
export function OptimizedImage() {
return (
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={630}
priority // Para imagens above-the-fold
placeholder="blur" // Blur placeholder
blurDataURL="data:image/..." // Base64 blur
sizes="(max-width: 768px) 100vw, 50vw"
/>
);
}
Font Optimization
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
const robotoMono = Roboto_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-roboto-mono',
});
export default function RootLayout({ children }) {
return (
<html lang="pt-BR" className={`${inter.variable} ${robotoMono.variable}`}>
<body>{children}</body>
</html>
);
}
Checklist de Migração
Antes de Migrar
- Fazer backup do projeto
- Criar branch de migração
- Atualizar Node.js para 18.17+
- Revisar breaking changes na documentação
Durante a Migração
- Atualizar dependências (next, react, react-dom)
- Rodar codemods automatizados
- Converter APIs síncronas para async
- Atualizar estratégias de cache
- Testar todas as rotas
Depois da Migração
- Verificar performance com Lighthouse
- Testar em diferentes dispositivos
- Monitorar erros em produção
Conclusão
O Next.js 15 é uma atualização transformadora que prepara o terreno para o futuro do desenvolvimento web com React. As novas APIs assíncronas, o Turbopack estável e o PPR representam avanços significativos em performance e DX.
Recursos Adicionais
Documentação:
Comunidade:
Última atualização: Dezembro 2024. Este guia será atualizado conforme novas features forem lançadas.

