Next.js
Pages Router: compatibilidad y legado
Explica el modelo del Pages Router, sus APIs de datos, routing, metadata, caché y compatibilidad con App Router en proyectos existentes.
- Última actualización
- Actualizada
- Nivel
- Aplicación
Next.js
Explica el modelo del Pages Router, sus APIs de datos, routing, metadata, caché y compatibilidad con App Router en proyectos existentes.
Pages Router sigue siendo una arquitectura válida para proyectos existentes. Su modelo se basa en archivos dentro de pages, data fetching mediante funciones especiales y una aplicación React principalmente cliente. Mantenerlo bien exige no mezclar sus APIs con las del App Router ni tratar una migración como requisito automático.
pages directory
├─ route file
├─ _app
├─ _document
├─ API routes
└─ data fetching function
↓
Next.js builds page
↓
HTML + page JSON + client bundleLas rutas se derivan de archivos:
pages/index.tsx → /
pages/products.tsx → /products
pages/products/[id].tsx → /products/:id
pages/api/orders.ts → /api/ordersPages Router usa:
getStaticProps.getServerSideProps.getStaticPaths._app.tsx._document.tsx.next/head.next/router.App Router usa:
generateMetadata.next/navigation.No escribas getServerSideProps dentro de app; no funciona allí.
Envuelve todas las páginas:
import type { AppProps } from "next/app";
import "@/styles/globals.css";
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}Se usa para:
Un _app gigante vuelve global lógica que quizá solo pertenece a una sección. Puedes usar patrones getLayout por página para layouts diferenciados.
Personaliza el documento HTML:
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="es">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}Solo se ejecuta en servidor y no maneja eventos ni datos de página. No lo uses para UI compartida; eso pertenece a _app o layouts propios.
Ejecuta durante build y puede regenerar:
export async function getStaticProps() {
const posts = await getPublishedPosts();
return {
props: { posts },
revalidate: 3600,
};
}Adecuado para contenido público y reutilizable.
Limitaciones:
No pases entidades ORM completas con Date, Decimal o secretos sin mapearlas.
Para rutas dinámicas estáticas:
export async function getStaticPaths() {
const products = await listPopularProducts();
return {
paths: products.map((product) => ({
params: { id: product.id },
})),
fallback: "blocking",
};
}fallback:
false: solo rutas generadas; las demás dan 404.true: fallback cliente inicial y generación posterior.blocking: primera request espera HTML completo.Elige según UX, volumen y hosting.
Ejecuta por request:
export async function getServerSideProps(context: GetServerSidePropsContext) {
const session = await getSession(context.req, context.res);
if (!session) {
return {
redirect: { destination: "/login", permanent: false },
};
}
const orders = await listOrders(session.userId);
return { props: { orders } };
}Aporta request, cookies y headers. Todo lo retornado se serializa al cliente, por lo que debes minimizar datos.
Una consulta lenta bloquea la página completa; no tiene el modelo granular de Server Components y Suspense del App Router.
Pages Router puede usar SWR o React Query:
const { data, error, isLoading } = useSWR("/api/orders", fetcher);Útil para:
Para contenido inicial, combinar SSR/SSG con revalidation cliente puede mejorar UX, pero evita duplicar ownership sin necesidad.
// pages/api/orders.ts
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "GET") {
res.setHeader("Allow", ["GET"]);
return res.status(405).end();
}
const orders = await listOrders();
return res.status(200).json({ data: orders });
}Son endpoints HTTP basados en Node req/res. Siguen siendo válidos para webhooks, APIs y uploads pequeños.
No llames una API Route propia desde getServerSideProps si puedes invocar el servicio directamente; añade HTTP interno innecesario.
useRouter:
import { useRouter } from "next/router";
const router = useRouter();
router.push("/products");router.query puede estar incompleto durante ciertos renders cliente. Usa tipos y estados de readiness cuando corresponda.
No importes hooks de next/navigation en pages esperando el mismo comportamiento.
import Head from "next/head";
<Head>
<title>Proyectos | Nicolás Garzón</title>
<meta name="description" content="..." />
</Head>next/head gestiona etiquetas por página. No existe la composición declarativa por segmentos de Metadata API.
Mantén canonical, OG y robots coherentes manualmente o mediante helpers.
Un proyecto Pages Router puede usar proxy.ts en Next.js 16 como frontera previa al routing. No autoriza por sí solo API Routes o getServerSideProps; cada entrada sensible valida sesión y permisos.
pages/404.tsx para 404 personalizada.pages/500.tsx para errores servidor._error.tsx para personalización avanzada.Los errores en getServerSideProps pueden producir 500. Modela not found mediante:
return { notFound: true };Pages Router usa modelos anteriores:
getStaticProps y revalidate.No apliques use cache, Cache Components o Server Actions dentro de pages como si fueran equivalentes universales.
Tipos útiles:
import type {
GetServerSideProps,
InferGetServerSidePropsType,
} from "next";export const getServerSideProps = (async () => {
return { props: { message: "Hola" } };
}) satisfies GetServerSideProps<{ message: string }>;export default function Page({
message,
}: InferGetServerSidePropsType<typeof getServerSideProps>) {
return <p>{message}</p>;
}Los tipos no validan query, cookies o APIs externas.
Revisa:
getServerSideProps globalmente lento.__NEXT_DATA__._app con providers pesados.Pages Router puede rendir muy bien si las páginas están bien segmentadas y las dependencias controladas.
El hecho de que getServerSideProps sea servidor no vuelve segura una consulta sin autorización.
pages y app pueden coexistir mientras rutas no colisionen. Esto permite migración incremental.
Considera:
Mantén una guía interna para saber qué modelo usa cada ruta.
Migra cuando existe valor concreto: layouts anidados, Server Components, streaming, Actions, reducción de JS o arquitectura futura.
getStaticProps dentro de app o generateMetadata dentro de pages.
Fuga y serialización enorme.
getServerSideProps llama /api propia.
Carga lógica global en todas las páginas.
Riesgo sin beneficio medido.
Sigue siendo soportado, aunque App Router sea la dirección principal.
pages._app envuelve UI; _document define documento.getStaticProps construye/revalida; getServerSideProps ejecuta por request._app y _document?fallback: "blocking"?getServerSideProps?pages con app?_app envuelve componentes React; _document personaliza HTML base del servidor.Migración de Pages Router a App Router transforma una vertical a la vez y compara comportamiento, seguridad y rendimiento.