Claude Code puede crear una página de Next.js en segundos. El problema es que esa página podría usar patrones de 2023.
Las herramientas de codificación con IA se entrenan con datasets masivos que incluyen años de código de Pages Router, patrones anteriores a App Router y prácticas de React desactualizadas. Cuando le pides a Claude Code que construya una funcionalidad de Next.js 16, a menudo genera código que parece correcto pero viola las convenciones de App Router de formas sutiles y difíciles de detectar.
Este artículo cubre los 7 errores más comunes de App Router que Claude Code genera, cómo prevenirlos con la configuración correcta de CLAUDE.md y cómo usar el servidor MCP integrado de Next.js 16 para un ciclo de feedback de errores en tiempo real.
Por Qué Claude Code Se Equivoca con App Router
El problema central es simple: los datos de entrenamiento de Claude contienen mucho más código de Pages Router que de App Router. Pages Router fue el estándar durante 7 años (2016–2023). App Router se estabilizó en Next.js 13.4 (mayo 2023) — apenas hace 3 años.
Esto significa que el "instinto predeterminado" de Claude Code se inclina hacia patrones como:
getServerSidePropsen lugar de Server Components asíncronosuseRouterdenext/routeren lugar denext/navigation- Route Handlers para todo en lugar de Server Actions para mutaciones
useEffectpara obtención de datos en lugar de acceso directo en Server Components
CLAUDE.md ayuda, pero no es suficiente por sí solo. Claude puede seguir tus reglas al inicio de la sesión y gradualmente volver a patrones antiguos a medida que el contexto se llena.
La solución real es una defensa de tres capas:
- CLAUDE.md — reglas explícitas que Claude lee al inicio de sesión
- AGENTS.md — dirige a Claude a los docs de Next.js incluidos en
node_modulespara referencia precisa por versión - next-devtools-mcp — alimenta errores de build y errores de tipo a Claude en tiempo real
Los 7 Errores Más Comunes de Next.js Generados por IA
Estos provienen del blog oficial de Vercel, reportes de la comunidad y tres meses de desarrollo diario con Claude Code + Next.js.
1. Llamar Route Handlers desde Server Components
// MAL — viaje de red innecesario
// app/users/page.tsx (Server Component)
export default async function UsersPage() {
const res = await fetch("http://localhost:3000/api/users");
const users = await res.json();
return <UserList users={users} />;
}
// BIEN — acceso directo a la base de datos en Server Component
// app/users/page.tsx (Server Component)
import { db } from "@/lib/db";
export default async function UsersPage() {
const users = await db.user.findMany();
return <UserList users={users} />;
}
Los Server Components se ejecutan en el servidor. Pueden acceder a bases de datos, sistemas de archivos y APIs internas directamente. Llamar a tu propio Route Handler crea una solicitud HTTP innecesaria a ti mismo.
2. Uso excesivo de "use client"
// MAL — toda la página se convierte en Client Component
"use client";
export default function DashboardPage() {
// Todo aquí se envía al navegador
}
// BIEN — solo el componente interactivo es Client Component
// app/dashboard/page.tsx (Server Component — sin directiva)
import { InteractiveChart } from "./interactive-chart";
export default async function DashboardPage() {
const data = await fetchDashboardData();
return (
<div>
<h1>Dashboard</h1>
<StaticSummary data={data} />
<InteractiveChart data={data} />
</div>
);
}
"use client" solo debe aparecer en los componentes hoja interactivos más pequeños. Claude tiende a agregarlo a archivos de nivel de página ante la primera señal de interactividad.
3. Ubicación incorrecta de Suspense Boundary
// MAL — Suspense envuelve el componente equivocado
export default function Page() {
return <AsyncDataComponent />;
}
async function AsyncDataComponent() {
const data = await fetchData();
return (
<Suspense fallback={<Loading />}>
<DataView data={data} />
</Suspense>
);
}
// BIEN — Suspense envuelve el componente asíncrono desde AFUERA
export default function Page() {
return (
<Suspense fallback={<Loading />}>
<AsyncDataComponent />
</Suspense>
);
}
async function AsyncDataComponent() {
const data = await fetchData();
return <DataView data={data} />;
}
Suspense debe envolver el componente que desencadena la operación asíncrona, desde el nivel padre. Colocarlo dentro del componente asíncrono anula su propósito.
4. Metadata en Client Components
// MAL — el export de metadata se ignora en Client Components
"use client";
export const metadata = { title: "Dashboard" };
// BIEN — metadata debe estar en un Server Component (page.tsx o layout.tsx)
// app/dashboard/page.tsx (sin "use client")
export const metadata = { title: "Dashboard" };
El export metadata solo funciona en Server Components. Si Claude agrega "use client" a un archivo de página, metadata deja de funcionar silenciosamente sin mensaje de error.
5. Falta de revalidation en Server Actions
// MAL — la UI no se actualiza después de la mutación
"use server";
export async function createPost(formData: FormData) {
await db.post.create({ data: { title: formData.get("title") as string } });
// La UI muestra datos obsoletos
}
// BIEN — revalidar la ruta afectada
"use server";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
await db.post.create({ data: { title: formData.get("title") as string } });
revalidatePath("/posts");
}
6. Usar redirect() dentro de try/catch
// MAL — redirect lanza internamente, es capturado por try/catch
"use server";
export async function loginAction(formData: FormData) {
try {
await authenticate(formData);
redirect("/dashboard"); // Esto lanza NEXT_REDIRECT, capturado abajo
} catch (error) {
return { error: "Login failed" }; // El redirect nunca ocurre
}
}
// BIEN — redirect fuera de try/catch
"use server";
export async function loginAction(formData: FormData) {
let success = false;
try {
await authenticate(formData);
success = true;
} catch (error) {
return { error: "Login failed" };
}
if (success) redirect("/dashboard");
}
redirect() funciona lanzando un error especial NEXT_REDIRECT. Si está dentro de try/catch, el redirect es absorbido.
7. Acceso síncrono a Request APIs (Next.js 16)
// MAL — acceso síncrono eliminado en Next.js 16
import { cookies } from "next/headers";
export default function Page() {
const cookieStore = cookies(); // Error de build en Next.js 16
}
// BIEN — acceso asíncrono requerido
import { cookies } from "next/headers";
export default async function Page() {
const cookieStore = await cookies();
}
Next.js 16 eliminó completamente el acceso síncrono a cookies(), headers(), params y searchParams. Claude frecuentemente genera la versión síncrona a partir de datos de entrenamiento antiguos.
Configuración de CLAUDE.md para Proyectos Next.js
Aquí hay un CLAUDE.md práctico que previene los errores anteriores.
# Project: Next.js 16 App Router
## Tech Stack
- Next.js 16.1 + TypeScript strict + Tailwind CSS v4
- App Router only. No Pages Router patterns
- React Server Components by default
## Critical Rules
- NEVER add "use client" to page.tsx or layout.tsx unless the entire page must be interactive
- NEVER call Route Handlers from Server Components — access data directly
- ALWAYS use async/await for cookies(), headers(), params, searchParams
- ALWAYS place Suspense boundaries OUTSIDE the async component
- ALWAYS call revalidatePath() or revalidateTag() in Server Actions after mutations
- NEVER put redirect() inside try/catch blocks
## Data Fetching
- Server Components: direct database/API access (no fetch to own Route Handlers)
- Client Components: Server Actions for mutations, Route Handlers only for third-party webhooks
- Cache: use the "use cache" directive for explicit caching
## Reference
- Read AGENTS.md for bundled Next.js docs location
- When unsure about an API, check node_modules/next/dist/docs/ first
AGENTS.md y Docs Incluidos
Next.js 16 incluye documentación dentro de node_modules/next/dist/docs/. El AGENTS.md generado automáticamente le indica a las herramientas de IA que consulten estos docs antes de generar código, asegurando orientación precisa por versión sin importar la antigüedad de los datos de entrenamiento.
Esta es la forma más efectiva de prevenir errores de "patrones antiguos." En lugar de depender de la memoria de Claude sobre las APIs de Next.js, lee los docs reales de la versión que tienes instalada.
next-devtools-mcp — El Puente de IA Integrado en Next.js 16
Next.js 16 incluye un endpoint /_next/mcp que expone el estado del servidor de desarrollo a herramientas de IA. El paquete next-devtools-mcp conecta Claude Code a este endpoint.
Configuración
Agrega a .mcp.json en la raíz de tu proyecto:
{
"mcpServers": {
"next-devtools": {
"command": "npx",
"args": ["-y", "next-devtools-mcp@latest"]
}
}
}
Luego inicia tu servidor de desarrollo (npm run dev) y Claude Code se conectará automáticamente.
Qué proporciona
| Herramienta | Qué hace |
|---|---|
get_errors | Retorna errores de build, errores de runtime y errores de tipo en tiempo real |
get_logs | Acceso a la salida de consola del servidor de desarrollo |
get_page_metadata | Estructura de rutas, tipos de componentes, modo de renderizado por página |
get_project_metadata | Configuración del proyecto, versión de Next.js, URL del servidor de desarrollo |
get_server_action_by_id | Localizar el archivo fuente de un Server Action específico |
Por qué esto importa
Sin MCP, el ciclo de feedback es: Claude escribe código → tú notas un error de build → lo pegas de vuelta → Claude lo arregla. Con next-devtools-mcp, Claude ve el error inmediatamente y puede auto-corregirse antes de que tú lo notes.
Esto es especialmente poderoso para el tipo de errores listados arriba. Una ubicación incorrecta de Suspense podría no lanzar un error visible pero aparecerá en get_page_metadata como comportamiento de renderizado inesperado.
Un Flujo de Trabajo Real con Claude Code × Next.js
Aquí está el flujo de trabajo que funciona para desarrollo diario de Next.js con Claude Code.
1. Configuración de sesión
# Iniciar servidor de desarrollo en segundo plano
npm run dev
# Iniciar Claude Code
claude
Claude Code se conecta a next-devtools-mcp automáticamente. Ejecuta /context para verificar que MCP está cargado.
2. Ciclo de desarrollo de funcionalidades
- Describe la funcionalidad que quieres
- Claude genera código — CLAUDE.md previene los 7 errores comunes
- next-devtools-mcp retroalimenta cualquier error en tiempo real
- Claude se auto-corrige sin necesidad de que pegues errores
- Revisa el código generado para corrección lógica
3. Validación pre-commit
Usa un PostToolUse Hook para ejecutar automáticamente la verificación de tipos después de ediciones:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx tsc --noEmit --pretty 2>&1 | head -20"
}
]
}
]
}
}
4. Gestión de contexto
/compactcuando trabajas en una funcionalidad por mucho tiempo/clearcuando cambias a una funcionalidad diferente- Session-handoff.md para trabajo multi-sesión
Para una inmersión profunda en estrategias de gestión de contexto, consulta "Por Qué Claude Code Olvida y Cómo Solucionarlo."
Conclusión
Claude Code es notablemente capaz en desarrollo de Next.js — una vez que le das las barreras correctas.
| Capa | Qué hace | Esfuerzo de configuración |
|---|---|---|
| CLAUDE.md | Previene los 7 errores comunes de App Router | 5 minutos |
| AGENTS.md | Fundamenta a Claude en docs de Next.js precisos por versión | Automático con create-next-app |
| next-devtools-mcp | Ciclo de feedback de errores en tiempo real | 2 minutos |
| Hooks | Ejecuta verificaciones de tipo automáticamente después de ediciones | 5 minutos |
El patrón es simple: restringir con CLAUDE.md, fundamentar con docs incluidos, validar con MCP. Configura estas tres capas correctamente y Claude Code dejará de generar patrones de 2023 para tu codebase de 2026.
Artículos relacionados: