Pages Router
File-System Routing
Vinext escanea el directorio pages/ y genera rutas automáticamente, igual que Next.js:
| Archivo | Ruta |
|---|---|
pages/index.tsx | / |
pages/about.tsx | /about |
pages/blog/index.tsx | /blog |
pages/blog/[slug].tsx | /blog/:slug |
pages/[...path].tsx | /:path* (catch-all) |
Página básica
import Head from "next/head";
import Link from "next/link";
export default function About() {
return (
<>
<Head>
<title>Acerca de</title>
</Head>
<h1>Acerca de nosotros</h1>
<Link href="/">Volver al inicio</Link>
</>
);
}
Los imports de next/head y next/link funcionan directamente — Vinext reimplementa estos módulos.
getServerSideProps
Ejecuta código en el servidor en cada request. Ideal para datos que cambian frecuentemente:
import type { GetServerSidePropsContext, GetServerSidePropsResult } from "next";
interface Props {
timestamp: string;
userAgent: string;
}
export async function getServerSideProps(
ctx: GetServerSidePropsContext
): Promise<GetServerSidePropsResult<Props>> {
return {
props: {
timestamp: new Date().toISOString(),
userAgent: ctx.req.headers["user-agent"] || "desconocido",
},
};
}
export default function SSRPage({ timestamp, userAgent }: Props) {
return (
<div>
<h1>Renderizado en servidor</h1>
<p>Hora: {timestamp}</p>
<p>User Agent: {userAgent}</p>
</div>
);
}
Respuestas alternativas
getServerSideProps puede retornar redirects o 404:
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const { id } = ctx.params!;
const data = await fetchData(id);
if (!data) {
return { notFound: true };
}
if (data.moved) {
return { redirect: { destination: data.newUrl, permanent: true } };
}
return { props: { data } };
}
Rutas dinámicas
Archivo: pages/posts/[id].tsx
import type { GetServerSidePropsContext } from "next";
import Link from "next/link";
interface Props {
id: string;
title: string;
}
export async function getServerSideProps(ctx: GetServerSidePropsContext) {
const id = ctx.params?.id as string;
return {
props: { id, title: `Post ${id}` },
};
}
export default function Post({ id, title }: Props) {
return (
<div>
<h1>{title}</h1>
<p>ID: {id}</p>
<Link href="/">Volver</Link>
</div>
);
}
getStaticProps y getStaticPaths
Para páginas que pueden pre-renderizarse en build time:
import type { GetStaticPaths, GetStaticProps } from "next";
interface Props {
slug: string;
content: string;
}
export const getStaticPaths: GetStaticPaths = async () => {
return {
paths: [
{ params: { slug: "primer-post" } },
{ params: { slug: "segundo-post" } },
],
fallback: false,
};
};
export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {
const slug = params?.slug as string;
return {
props: {
slug,
content: `Contenido de ${slug}`,
},
revalidate: 60, // ISR: regenerar cada 60 segundos
};
};
export default function BlogPost({ slug, content }: Props) {
return (
<article>
<h1>{slug}</h1>
<p>{content}</p>
</article>
);
}
Opciones de fallback
| Valor | Comportamiento |
|---|---|
false | Solo paths definidos, 404 para el resto |
true | Muestra fallback mientras genera, luego cachea |
"blocking" | SSR en primera visita, luego cachea |
API Routes
Archivo: pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Método no permitido" });
}
res.json({
message: "Hola desde la API",
timestamp: new Date().toISOString(),
});
}
Las API routes en pages/api/ funcionan exactamente como en Next.js, con acceso completo a req y res.
_app.tsx y _document.tsx
Vinext soporta los archivos especiales de Pages Router:
// pages/_app.tsx
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
Siguiente paso
En el siguiente capítulo exploraremos el App Router: layouts anidados, loading states, error boundaries y route groups.