Conceptos básicos de App Router
El App Router utiliza un enrutador basado en el sistema de archivos donde las carpetas definen rutas y los archivos especiales definen la interfaz de usuario y el comportamiento.
Busca en todas las páginas de la documentación
El App Router utiliza un enrutador basado en el sistema de archivos donde las carpetas definen rutas y los archivos especiales definen la interfaz de usuario y el comportamiento.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de referencia rápida - lista para copiar y pegar.
app/
├── layout.tsx # Layout raíz (requerido)
├── page.tsx # Ruta de inicio → /
├── loading.tsx # Interfaz de carga para /
├── error.tsx # Límite de error para /
├── not-found.tsx # Interfaz 404 para /
├── about/
│ └── page.tsx # /about
├── blog/
│ ├── layout.tsx # Layout anidado para /blog/*
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/:slug
└── api/
└── health/
└── route.tsx # GET /api/health
Regla clave: Una ruta es solo públicamente accesible cuando una carpeta contiene un archivo page.tsx o route.tsx.
// app/layout.tsx - Layout raíz (requerido, envuelve cada página)
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "My App",
description: "Built with Next.js App Router",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}// app/page.tsx - Página de inicio (Server Component por defecto)
export default function HomePage() {
return (
<main>
<h1>Welcome</h1>
<p>This is a Server Component - no client JS shipped.</p>
</main>
);
}// app/dashboard/page.tsx - Ruta anidada en /dashboard
export default function DashboardPage() {
return <h1>Dashboard</h1>;
}// app/api/health/route.tsx - Route Handler (endpoint de API)
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ status: "ok", timestamp: Date.now() });
}Cuándo usarlo: Cada proyecto de Next.js 15+ usa el App Router. Comienza aquí cuando crees cualquier ruta nueva, layout o endpoint de API.
app/ se asigna a un segmento de URL. app/blog/settings/page.tsx sirve /blog/settings.page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx, route.tsx, template.tsx y default.tsx."use client" en la parte superior.route.tsx y page.tsx no pueden coexistir en la misma carpeta. Un segmento de ruta es una página o una ruta de API, no ambas.loading.tsx crea un límite <Suspense> automático. Next.js envuelve la página en Suspense usando loading.tsx como fallback.error.tsx crea un Error Boundary automático. Captura errores en la página y sus hijos pero no en el layout del mismo nivel.// app/template.tsx - Como layout, pero se remonta en cada navegación
export default function Template({ children }: { children: React.ReactNode }) {
return <div className="animate-fade-in">{children}</div>;
}// app/not-found.tsx - Página global 404
export default function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<p>The page you are looking for does not exist.</p>
</div>
);
}// app/api/users/route.tsx - Manejador de ruta con múltiples métodos
import { NextRequest, NextResponse } from "next/server";
export async function GET() {
const users = await db.user.findMany();
return NextResponse.json(users);
}
export async function POST(request: NextRequest) {
const body = await request.json();
const user = await db.user.create({ data: body });
return NextResponse.json(user, { status: 201 });
}// Next.js proporciona tipos integrados para props de página y layout
// Los props de página en Next.js 15+ usan params basados en Promise
interface PageProps {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}
// Los props de layout siempre incluyen children
interface LayoutProps {
children: React.ReactNode;
params: Promise<{ slug: string }>;
}layout.tsx no se re-renderiza en la navegación. Si necesitas estado fresco en cada navegación, usa template.tsx en su lugar.error.tsx no captura errores en el layout del mismo nivel. Para capturar errores de layout, coloca error.tsx en el segmento padre.route.tsx debe exportar métodos HTTP nombrados (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). Las exportaciones predeterminadas se ignoran.await params dentro de páginas y layouts - la API sincrónica está deprecada.searchParams también es asincrónico en Next.js 15+. Usa await searchParams o use(searchParams) - no desestructures sincrónicamente.not-found.tsx en la raíz captura todas las rutas sin coincidencias. El not-found.tsx a nivel de segmento solo se activa cuando llamas a notFound().| Enfoque | Cuándo usar |
|---|---|
Pages Router (directorio pages/) | Proyectos legados no migrados aún |
Manejador route.tsx | Endpoints solo de API sin interfaz de usuario |
template.tsx | Necesitas una instancia de componente fresca en cada navegación |
| Enrutador de terceros (TanStack Router) | Aplicaciones React no de Next.js que necesitan enrutamiento type-safe |
Una ruta es solo públicamente accesible cuando una carpeta contiene un archivo page.tsx o route.tsx. Otros archivos (componentes, utils, estilos) colocados en la carpeta no se exponen como rutas.
No. Un segmento de ruta es una página o una ruta de API, nunca ambas. Coloca tu route.tsx en una carpeta separada (p. ej., app/api/health/route.tsx).
Server Components por defecto. Debes agregar "use client" en la parte superior de un archivo para convertirlo en un Client Component.
layout.tsx persiste en las navegaciones y no se remontatemplate.tsx se remonta en cada navegación, dando estado fresco cada veztemplate.tsx para animaciones de entrada/salida o logging por navegaciónEl límite de error creado por error.tsx envuelve la página, no el layout hermano. Para capturar errores de layout, coloca error.tsx en el segmento padre.
Next.js envuelve automáticamente la página en <Suspense fallback={<Loading />}>. La interfaz de carga se muestra instantáneamente mientras la página se carga.
Se rompe. Tanto params como searchParams ahora son objetos Promise en Next.js 15+. Debes await params dentro de páginas y layouts. La API sincrónica está deprecada.
// app/api/users/route.tsx
import { NextRequest, NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({ users: [] });
}
export async function POST(request: NextRequest) {
const body = await request.json();
return NextResponse.json(body, { status: 201 });
}not-found.tsx raíz captura todas las rutas sin coincidencias automáticamentenot-found.tsx a nivel de segmento solo se activa cuando llamas a notFound() desde next/navigationinterface PageProps {
params: Promise<{ slug: string }>;
searchParams: Promise<{
[key: string]: string | string[] | undefined;
}>;
}
interface LayoutProps {
children: React.ReactNode;
params: Promise<{ slug: string }>;
}import { NextRequest, NextResponse } from "next/server";
export async function GET(): Promise<NextResponse> {
return NextResponse.json({ status: "ok" });
}
export async function POST(
request: NextRequest
): Promise<NextResponse> {
const body = await request.json();
return NextResponse.json(body, { status: 201 });
}Se ignora. route.tsx debe exportar métodos HTTP nombrados (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS). Las exportaciones predeterminadas no hacen nada.
Sí. Los archivos no especiales (componentes, utils, estilos, pruebas) colocados dentro de carpetas de ruta no se exponen como rutas. Solo los archivos con nombres reservados como page.tsx y route.tsx se tratan como rutas.
Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥