Volver a Next.js Básico
Enrutamiento Basado en Archivos
El App Router de Next.js usa el sistema de archivos para definir rutas. Cada page.tsx dentro de app/ se convierte en una URL públicamente accesible.
Convenciones de Rutas
| Archivo | URL |
|---|---|
app/page.tsx | / |
app/sobre/page.tsx | /sobre |
app/blog/[slug]/page.tsx | /blog/mi-post |
app/tienda/[...ruta]/page.tsx | /tienda/a/b/c |
app/(marketing)/page.tsx | / (grupo, sin impacto en URL) |
Rutas Dinámicas
// app/blog/[slug]/page.tsx
interface Props {
params: Promise<{ slug: string }>;
}
export default async function BlogPost({ params }: Props) {
const { slug } = await params;
const post = await obtenerPost(slug);
return (
<article>
<h1>{post.titulo}</h1>
<div dangerouslySetInnerHTML={{ __html: post.contenido }} />
</article>
);
}
// Generar rutas estáticas en tiempo de compilación
export async function generateStaticParams() {
const posts = await obtenerTodosPosts();
return posts.map(post => ({ slug: post.slug }));
}
Layouts Anidados
// app/blog/layout.tsx — envuelve todas las rutas /blog/*
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex">
<aside className="w-64">
<BarraLateralBlog />
</aside>
<main className="flex-1">{children}</main>
</div>
);
}
Navegación
import Link from 'next/link';
import { useRouter } from 'next/navigation';
// Declarativa — preferida
export function NavLinks() {
return (
<nav>
<Link href="/">Inicio</Link>
<Link href="/sobre">Sobre mí</Link>
<Link href="/blog/mi-post" prefetch={false}>Post</Link>
</nav>
);
}
// Programática (solo en Componentes de Cliente)
'use client';
export function Volver() {
const router = useRouter();
return <button onClick={() => router.back()}>← Volver</button>;
}
Estados de Carga y Error
// app/blog/loading.tsx — mostrado mientras la página carga
export default function Cargando() {
return <div className="skeleton">Cargando posts...</div>;
}
// app/blog/error.tsx — mostrado cuando ocurre un error
'use client';
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>¡Algo salió mal!</h2>
<p>{error.message}</p>
<button onClick={reset}>Intentar de nuevo</button>
</div>
);
}
Grupos de Rutas
Usa paréntesis (nombre) para organizar rutas sin afectar la URL:
app/
├── (marketing)/
│ ├── layout.tsx # Layout para marketing
│ ├── page.tsx # /
│ └── sobre/page.tsx # /sobre
└── (dashboard)/
├── layout.tsx # Layout del dashboard (protegido con auth)
└── dashboard/page.tsx # /dashboard