# Live Preview en Payload con Next.js

> Cómo ver el artículo real mientras lo escribes en el panel de Payload: la URL de vista previa, una ruta que activa Draft Mode y un componente que refresca la página.

URL: https://codegea.com/live-preview-en-payload-con-next-js
CMS · 2026-10-19 · Derian Pinto

Live Preview es la pantalla dividida del panel de Payload: a un lado el formulario, al otro tu sitio real mostrando el contenido que estás escribiendo. Para un editor es la diferencia entre publicar a ciegas y ver el resultado antes. Montarlo con Next.js son tres piezas pequeñas.

## Cómo funciona

El panel carga tu sitio dentro de un `iframe`. Para que ese iframe muestre un **borrador** y no el contenido publicado, hay que pedirle a Next.js que active el *Draft Mode*. Cada vez que guardas, la página debe refrescarse. Las tres piezas:

1. Una **URL de vista previa** en la configuración de Payload.
2. Una **ruta de API** que comprueba tu sesión y activa Draft Mode.
3. Un **componente cliente** en la página que refresca al guardar.

## Pieza 1: la URL en la configuración

En `payload.config.ts`:

```ts
admin: {
  livePreview: {
    collections: ['posts', 'projects'],
    breakpoints: [
      { label: 'Móvil', name: 'mobile', width: 375, height: 667 },
      { label: 'Tablet', name: 'tablet', width: 768, height: 1024 },
      { label: 'Escritorio', name: 'desktop', width: 1440, height: 900 },
    ],
    url: ({ data, collectionConfig, locale }) => {
      if (!data?.slug || !collectionConfig) return null;
      const params = new URLSearchParams({
        collection: collectionConfig.slug,
        slug: String(data.slug),
        locale: locale.code,
      });
      return `/api/preview?${params}`;
    },
  },
},
```

La vista previa no apunta directo a la página del artículo, sino a una ruta intermedia, `/api/preview`, a la que le pasa la colección, el slug y el idioma. Si el documento aún no tiene slug, devuelve `null` y no hay vista previa. Los `breakpoints` añaden los botones de móvil, tablet y escritorio.

## Pieza 2: la ruta que activa Draft Mode

En `app/api/preview/route.ts`:

```ts
export async function GET(req: NextRequest) {
  const params = req.nextUrl.searchParams;
  const draft = await draftMode();

  if (params.get('exit')) {
    draft.disable();
    redirect('/');
  }

  const payload = await payloadClient();
  const { user } = await payload.auth({ headers: req.headers });
  const collection = params.get('collection');
  const slug = params.get('slug');
  const locale = params.get('locale');

  if (!user || !slug || !isLocale(locale) ||
      (collection !== 'posts' && collection !== 'projects')) {
    return new Response('Forbidden', { status: 403 });
  }

  draft.enable();
  redirect(collection === 'posts' ? postPath(locale, slug) : projectPath(locale, slug));
}
```

Esta ruta **exige sesión del panel**. `payload.auth({ headers })` comprueba la cookie, y como el iframe está en el mismo origen, la cookie viaja sola. Sin sesión, 403: nadie puede ver tus borradores adivinando la URL. Con sesión, activa Draft Mode y redirige a la URL real del artículo, construida con la misma función que usa el resto del sitio. `?exit=1` lo desactiva.

## Pieza 3: la página lee borradores y se refresca

En la página del artículo, preguntas si Draft Mode está activo y se lo pasas a la consulta:

```ts
const { isEnabled: draft } = await draftMode();
const post = await getPost({ slug, locale, draft });
// …
{draft && <LivePreviewListener />}
```

Con `draft` en `true`, la consulta pide la última versión guardada como borrador, no la publicada. Y `LivePreviewListener` es un pequeño componente cliente:

```ts
'use client';

import { RefreshRouteOnSave } from '@payloadcms/live-preview-react';
import { useRouter } from 'next/navigation';

export function LivePreviewListener() {
  const router = useRouter();
  return <RefreshRouteOnSave refresh={() => router.refresh()} serverURL={SITE_URL} />;
}
```

Cada vez que se guarda en el panel, `RefreshRouteOnSave` llama a `router.refresh()` y Next.js vuelve a renderizar la página en el servidor, con el borrador nuevo.

## Dos cosas que conviene saber

- **Refresca al guardar, no al teclear.** Esta variante usa el refresco del servidor, que es la más simple y no exige que tu componente sepa leer datos en el cliente.
- **El panel y el sitio deben compartir origen.** Si el panel está en otro dominio, la cookie de sesión no viaja en el iframe y hay que resolver la autenticación de otra manera.

## Para qué sirve en la práctica

Quien escribe ve al instante cómo queda un título largo, una tabla o un bloque de código en móvil, sin publicar. Es una de las razones por las que elegimos Payload; el resto está en [Payload vs Strapi vs Directus](/payload-cms-vs-strapi-vs-directus-en-2026), y cómo encaja en el sitio, en [así está construido codegea.com](/asi-esta-construido-codegea-nextjs-payload-turso-r2).
