🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Next.js 15 Guia completo 2026: App Router, componentes de servidor e PPR

⏱️5 min read  ·  1,067 words

Next.js 15 solidifica o futuro full-stack do React. Com o App Router estável, React Server Components como padrão e Turbopack substituindo Webpack, construir aplicativos Next.js de produção em 2026 é mais rápido e poderoso do que nunca.

O que há de novo no Next.js 15

  • Turbopack estável— Construções 10x mais rápidas, HMR 700x mais rápido vs Webpack
  • Suporte de primeira classe para React 19— Componentes do servidor, ações, gancho use()
  • Pré-renderização parcial (PPR)— shell estático + ilhas dinâmicas na mesma página
  • Cache aprimorado– não há mais comportamento surpreendente de cache por padrão
  • APIs dinâmicas— cookies(), headers(), searchParams agora são assíncronos

Configuração do projeto

# Create new Next.js 15 project
npx create-next-app@latest my-app --typescript --tailwind --app --turbopack

cd my-app

# Project structure
# app/
#   layout.tsx          — root layout
#   page.tsx            — home page
#   globals.css
#   (dashboard)/        — route group (no URL segment)
#     page.tsx
#   blog/
#     [slug]/
#       page.tsx        — dynamic route
#   api/
#     users/
#       route.ts        — API route handler

Fundamentos do roteador de aplicativos

// app/layout.tsx — Root Layout (required)
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: { template: "%s | My App", default: "My App" },
  description: "Built with Next.js 15",
  openGraph: {
    type: "website",
    locale: "en_US",
    url: "https://myapp.com",
    siteName: "My App",
  },
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

Componentes de servidor versus componentes de cliente

// Server Component (default) — runs on server, no JavaScript sent to client
// app/blog/page.tsx
async function BlogPage() {
  // Direct database/API access — no useEffect needed
  const posts = await db.query("SELECT * FROM posts ORDER BY created_at DESC");

  return (
    <main>
      <h1>Blog</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </main>
  );
}

// Client Component — add "use client" directive
"use client";
import { useState } from "react";

function SearchBar({ onSearch }: { onSearch: (q: string) => void }) {
  const [query, setQuery] = useState("");

  return (
    <input
      value={query}
      onChange={e => setQuery(e.target.value)}
      onKeyDown={e => e.key === "Enter" && onSearch(query)}
      placeholder="Search..."
    />
  );
}

Busca de dados com componentes de servidor

// Parallel data fetching — no waterfalls
async function Dashboard() {
  // These run in parallel
  const [user, posts, stats] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchStats(),
  ]);

  return (
    <div>
      <UserCard user={user} />
      <PostList posts={posts} />
      <StatsPanel stats={stats} />
    </div>
  );
}

// Dynamic rendering with searchParams (Next.js 15: async)
export default async function SearchPage({
  searchParams,
}: {
  searchParams: Promise<{ q?: string; page?: string }>;
}) {
  const { q = "", page = "1" } = await searchParams;
  const results = await searchPosts(q, parseInt(page));

  return <SearchResults results={results} query={q} />;
}

Ações do servidor

As ações do servidor permitem executar o código do servidor a partir de formulários e manipuladores de eventos:

// app/actions.ts
"use server";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

export async function createPost(formData: FormData) {
  const title = formData.get("title") as string;
  const content = formData.get("content") as string;

  // Validate
  if (!title || title.length < 3) {
    return { error: "Title must be at least 3 characters" };
  }

  // Save to database
  await db.posts.create({ title, content });

  // Revalidate and redirect
  revalidatePath("/blog");
  redirect("/blog");
}

// Use in a form
export default function NewPostForm() {
  return (
    <form action={createPost}>
      <input name="title" placeholder="Title" required />
      <textarea name="content" placeholder="Content" />
      <button type="submit">Publish</button>
    </form>
  );
}

Manipuladores de rota (rotas de API)

// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function GET(request: NextRequest) {
  const { searchParams } = request.nextUrl;
  const page = parseInt(searchParams.get("page") ?? "1");

  const users = await db.users.findMany({
    skip: (page - 1) * 10,
    take: 10,
  });

  return NextResponse.json({ users, page });
}

export async function POST(request: NextRequest) {
  const body = await request.json();
  const user = await db.users.create({ data: body });
  return NextResponse.json(user, { status: 201 });
}

// app/api/users/[id]/route.ts
export async function DELETE(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  const { id } = await params;
  await db.users.delete({ where: { id } });
  return new NextResponse(null, { status: 204 });
}

Pré-renderização parcial (PPR)

PPR permite combinar conteúdo estático e dinâmico em uma única página:

// next.config.ts
import type { NextConfig } from "next";

const config: NextConfig = {
  experimental: {
    ppr: true,
  },
};

export default config;

// page.tsx — static shell + dynamic island
import { Suspense } from "react";

export default function ProductPage({ params }: { params: { id: string } }) {
  return (
    <div>
      {/* Static — generated at build time */}
      <ProductDetails id={params.id} />

      {/* Dynamic — rendered per request */}
      <Suspense fallback={<PriceSkeleton />}>
        <DynamicPrice id={params.id} />
      </Suspense>

      <Suspense fallback={<ReviewsSkeleton />}>
        <UserReviews id={params.id} />
      </Suspense>
    </div>
  );
}

Middleware

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  const token = request.cookies.get("auth-token")?.value;
  const isProtected = request.nextUrl.pathname.startsWith("/dashboard");

  if (isProtected && !token) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  // Add security headers
  const response = NextResponse.next();
  response.headers.set("X-Frame-Options", "DENY");
  response.headers.set("X-Content-Type-Options", "nosniff");

  return response;
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};

Otimização de imagem

import Image from "next/image";

// Optimized image — auto WebP/AVIF, lazy loading, size hints
export function HeroImage({ src, alt }: { src: string; alt: string }) {
  return (
    <Image
      src={src}
      alt={alt}
      width={1200}
      height={630}
      priority // LCP image — eager load
      quality={85}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,/9j..." // low-quality placeholder
    />
  );
}

// Fill container
<div style={{ position: "relative", aspectRatio: "16/9" }}>
  <Image src="/hero.jpg" alt="Hero" fill sizes="100vw" />
</div>

Implantação e Produção

  • Vercel— configuração zero, rede de borda, PPR automático
  • Auto-hospedadonext build && next startcom Node.js
  • Docker– Dockerfile Next.js oficial com saída independente

Next.js 15 é a estrutura React mais completa para 2026. Os componentes do servidor eliminam a busca excessiva, as ações do servidor simplificam as mutações e o PPR oferece o melhor em renderização estática e dinâmica na mesma página.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা