Pages Router

Por: Artiko
vinextpages-routerroutingssr

File-System Routing

Vinext escanea el directorio pages/ y genera rutas automáticamente, igual que Next.js:

ArchivoRuta
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

ValorComportamiento
falseSolo paths definidos, 404 para el resto
trueMuestra 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.