Modo de vista previa

Nota: Esta característica ha sido reemplazada por Modo borrador.

Ejemplos

En la documentación de Páginas y la documentación de Obtención de datos, explicamos cómo prerrenderizar una página en tiempo de compilación (Generación estática) usando getStaticProps y getStaticPaths.

La Generación estática es útil cuando tus páginas obtienen datos de un CMS headless. Sin embargo, no es ideal cuando estás escribiendo un borrador en tu CMS headless y quieres previsualizarlo inmediatamente en tu página. En este caso, necesitarías que Next.js renderice estas páginas en tiempo de solicitud en lugar de tiempo de compilación, y que obtenga el contenido del borrador en lugar del contenido publicado. Querrías que Next.js evite la Generación estática solo para este caso específico.

Next.js tiene una característica llamada Modo de vista previa que resuelve este problema. A continuación, se indican las instrucciones para usarlo.

Paso 1: Crear y acceder a una ruta API de vista previa

Consulta primero la documentación de Rutas API si no estás familiarizado con las Rutas API de Next.js.

Primero, crea una ruta API de vista previa. Puede tener cualquier nombre, por ejemplo pages/api/preview.js (o .ts si usas TypeScript).

En esta ruta API, necesitas llamar a setPreviewData en el objeto de respuesta. El argumento para setPreviewData debe ser un objeto, y este puede ser usado por getStaticProps (más sobre esto más adelante). Por ahora, usaremos {}.

export default function handler(req, res) {
  // ...
  res.setPreviewData({})
  // ...
}

res.setPreviewData establece algunas cookies en el navegador que activan el modo de vista previa. Cualquier solicitud a Next.js que contenga estas cookies se considerará en modo de vista previa, y el comportamiento para páginas generadas estáticamente cambiará (más sobre esto más adelante).

Puedes probar esto manualmente creando una ruta API como la siguiente y accediendo a ella manualmente desde tu navegador:

pages/api/preview.js
// Ejemplo simple para probarlo manualmente desde tu navegador.
export default function handler(req, res) {
  res.setPreviewData({})
  res.end('Modo de vista previa activado')
}

Si abres las herramientas de desarrollador de tu navegador y visitas /api/preview, notarás que las cookies __prerender_bypass y __next_preview_data se establecerán en esta solicitud.

Acceso seguro desde tu CMS headless

En la práctica, querrás llamar a esta ruta API de forma segura desde tu CMS headless. Los pasos específicos variarán según el CMS headless que uses, pero aquí hay algunos pasos comunes que podrías seguir.

Estos pasos asumen que el CMS headless que usas admite la configuración de URLs de vista previa personalizadas. Si no es así, aún puedes usar este método para proteger tus URLs de vista previa, pero necesitarás construir y acceder a la URL de vista previa manualmente.

Primero, debes crear una cadena de token secreto usando un generador de tokens de tu elección. Este secreto solo lo conocerán tu aplicación Next.js y tu CMS headless. Este secreto evita que personas que no tienen acceso a tu CMS accedan a las URLs de vista previa.

Segundo, si tu CMS headless admite configurar URLs de vista previa personalizadas, especifica lo siguiente como la URL de vista previa. Esto asume que tu ruta API de vista previa está ubicada en pages/api/preview.js.

Terminal
https://<tu-sitio>/api/preview?secret=<token>&slug=<ruta>
  • <tu-sitio> debe ser tu dominio de despliegue.
  • <token> debe ser reemplazado con el token secreto que generaste.
  • <ruta> debe ser la ruta de la página que quieres previsualizar. Si quieres previsualizar /posts/foo, entonces debes usar &slug=/posts/foo.

Tu CMS headless podría permitirte incluir una variable en la URL de vista previa para que <ruta> pueda establecerse dinámicamente según los datos del CMS, así: &slug=/posts/{entry.fields.slug}

Finalmente, en la ruta API de vista previa:

  • Verifica que el secreto coincida y que exista el parámetro slug (si no, la solicitud debe fallar).
  • Llama a res.setPreviewData.
  • Luego redirige el navegador a la ruta especificada por slug. (El siguiente ejemplo usa una redirección 307).
export default async (req, res) => {
  // Verifica el secreto y los parámetros
  // Este secreto solo debe ser conocido por esta ruta API y el CMS
  if (req.query.secret !== 'MI_TOKEN_SECRETO' || !req.query.slug) {
    return res.status(401).json({ message: 'Token inválido' })
  }

  // Consulta el CMS headless para verificar si existe el `slug` proporcionado
  // getPostBySlug implementaría la lógica necesaria para consultar el CMS headless
  const post = await getPostBySlug(req.query.slug)

  // Si el slug no existe, evita que se active el modo de vista previa
  if (!post) {
    return res.status(401).json({ message: 'Slug inválido' })
  }

  // Activa el Modo de vista previa estableciendo las cookies
  res.setPreviewData({})

  // Redirige a la ruta del post obtenido
  // No redirigimos a req.query.slug para evitar vulnerabilidades de redirección abierta
  res.redirect(post.slug)
}

Si tiene éxito, el navegador será redirigido a la ruta que deseas previsualizar con las cookies del modo de vista previa establecidas.

Paso 2: Actualizar getStaticProps

El siguiente paso es actualizar getStaticProps para que admita el modo de vista previa.

Si solicitas una página que tiene getStaticProps con las cookies del modo de vista previa establecidas (a través de res.setPreviewData), entonces getStaticProps se llamará en tiempo de solicitud (en lugar de tiempo de compilación).

Además, se llamará con un objeto context donde:

  • context.preview será true.
  • context.previewData será el mismo que el argumento usado para setPreviewData.
export async function getStaticProps(context) {
  // Si solicitas esta página con las cookies del modo de vista previa establecidas:
  //
  // - context.preview será true
  // - context.previewData será el mismo que
  //   el argumento usado para `setPreviewData`.
}

Usamos res.setPreviewData({}) en la ruta API de vista previa, por lo que context.previewData será {}. Puedes usar esto para pasar información de sesión desde la ruta API de vista previa a getStaticProps si es necesario.

Si también usas getStaticPaths, entonces context.params también estará disponible.

Obtener datos de vista previa

Puedes actualizar getStaticProps para obtener datos diferentes según context.preview y/o context.previewData.

Por ejemplo, tu CMS headless podría tener un endpoint API diferente para posts en borrador. Si es así, puedes usar context.preview para modificar la URL del endpoint API como se muestra a continuación:

export async function getStaticProps(context) {
  // Si context.preview es true, añade "/preview" al endpoint API
  // para solicitar datos de borrador en lugar de datos publicados. Esto variará
  // según el CMS headless que uses.
  const res = await fetch(`https://.../${context.preview ? 'preview' : ''}`)
  // ...
}

¡Eso es todo! Si accedes a la ruta API de vista previa (con secret y slug) desde tu CMS headless o manualmente, ahora deberías poder ver el contenido de vista previa. Y si actualizas tu borrador sin publicarlo, deberías poder previsualizar el borrador.

Configura esto como la URL de vista previa en tu CMS headless o accede manualmente, y deberías poder ver la vista previa.

Terminal
https://<tu-sitio>/api/preview?secret=<token>&slug=<ruta>

Más detalles

Bueno saber: durante el renderizado, next/router expone una bandera isPreview, consulta la documentación del objeto router para más información.

Especificar la duración del Modo de vista previa

setPreviewData acepta un segundo parámetro opcional que debe ser un objeto de opciones. Acepta las siguientes claves:

  • maxAge: Especifica el número (en segundos) que durará la sesión de vista previa.
  • path: Especifica la ruta bajo la cual se aplicará la cookie. Por defecto es /, lo que activa el modo de vista previa para todas las rutas.
setPreviewData(data, {
  maxAge: 60 * 60, // Las cookies del modo de vista previa expiran en 1 hora
  path: '/about', // Las cookies del modo de vista previa se aplican a rutas con /about
})

Limpiar las cookies del Modo de vista previa

Por defecto, no se establece una fecha de expiración para las cookies del Modo de vista previa, por lo que la sesión de vista previa termina cuando se cierra el navegador.

Para limpiar las cookies del Modo de vista previa manualmente, crea una ruta API que llame a clearPreviewData():

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  res.clearPreviewData({})
}

Luego, envía una solicitud a /api/clear-preview-mode-cookies para invocar la Ruta API. Si llamas a esta ruta usando next/link, debes pasar prefetch={false} para evitar llamar a clearPreviewData durante la precarga de enlaces.

Si se especificó una ruta en la llamada a setPreviewData, debes pasar la misma ruta a clearPreviewData:

pages/api/clear-preview-mode-cookies.js
export default function handler(req, res) {
  const { path } = req.query

  res.clearPreviewData({ path })
}

Límites de tamaño de previewData

Puedes pasar un objeto a setPreviewData y que esté disponible en getStaticProps. Sin embargo, como los datos se almacenarán en una cookie, hay un límite de tamaño. Actualmente, los datos de vista previa están limitados a 2KB.

Funciona con getServerSideProps

El modo de vista previa también funciona con getServerSideProps. También estará disponible en el objeto context que contiene preview y previewData.

Bueno saber: No debes establecer la cabecera Cache-Control cuando uses el Modo de vista previa porque no se puede omitir. En su lugar, recomendamos usar ISR.

Funciona con Rutas API

Las Rutas API tendrán acceso a preview y previewData bajo el objeto de solicitud. Por ejemplo:

export default function myApiRoute(req, res) {
  const isPreview = req.preview
  const previewData = req.previewData
  // ...
}

Único por cada next build

Tanto el valor de la cookie de bypass como la clave privada para encriptar previewData cambian cuando se completa next build. Esto asegura que la cookie de bypass no pueda ser adivinada.

Bueno saber: Para probar el Modo de vista previa localmente sobre HTTP, tu navegador necesitará permitir cookies de terceros y acceso al almacenamiento local.