Back

Mostrando PDFs en Aplicaciones Vue 3

Mostrando PDFs en Aplicaciones Vue 3

Integrar un PDF dentro de una SPA de Vue 3 suena sencillo—hasta que te encuentras con tu primer error CORS, descubres que el navegador lo renderiza de manera diferente en Safari, o te das cuenta de que tu documento de 200 páginas está bloqueando el hilo principal. Este artículo cubre los tres enfoques prácticos que los desarrolladores realmente utilizan para mostrar PDFs en Vue 3, con ventajas y desventajas honestas para cada uno.

Puntos Clave

  • Los tres enfoques principales para mostrar PDFs en Vue 3 son las incrustaciones nativas del navegador (<iframe>/<embed>), la integración directa de PDF.js, y los componentes wrapper específicos de Vue.
  • Las incrustaciones nativas no añaden peso al bundle pero no ofrecen control sobre estilos, barras de herramientas o consistencia entre navegadores.
  • PDF.js te da control total sobre el renderizado pero añade una carga notable del lado del cliente—cárgalo de forma diferida con import() dinámico para mantener tiempos de carga inicial rápidos.
  • Los wrappers de Vue PDF como vue-pdf-embed reducen significativamente el código repetitivo a costa de algo de flexibilidad en la personalización.
  • La configuración correcta del worker y la carga diferida son los dos pasos que previenen los problemas más comunes relacionados con PDFs en aplicaciones Vue.

Los Tres Enfoques Principales

1. Incrustación Nativa del Navegador: <iframe> o <embed>

La forma más rápida de mostrar un PDF en pantalla es delegarlo al renderizador de PDF integrado del navegador.

<template>
  <iframe
    :src="pdfUrl"
    width="100%"
    height="700px"
    style="border: none"
  />
</template>

<script setup lang="ts">
const pdfUrl = '/documents/report.pdf'
</script>

Cuándo funciona bien: Herramientas internas, paneles de administración, o cualquier contexto donde la consistencia de UI no sea crítica y el PDF se sirva desde el mismo origen.

Las limitaciones reales:

  • Cero control sobre la barra de herramientas, controles de zoom o tematización
  • El comportamiento varía entre navegadores—Chrome, Firefox y Safari renderizan de manera diferente
  • Los navegadores móviles a menudo descargan el archivo en lugar de mostrarlo en línea
  • El estado de carga es invisible para tu componente Vue, por lo que no puedes mostrar un spinner o manejar errores de manera elegante

Si estás cargando un PDF desde un dominio diferente, el navegador bloqueará la solicitud a menos que el servidor envíe los encabezados Access-Control-Allow-Origin correctos. Un endpoint proxy del mismo origen en tu backend es la solución más limpia.


2. PDF.js con Vue 3: Integración Directa

PDF.js es el motor de renderizado de PDF de código abierto de Mozilla. A partir de la serie de versiones 5.x, se distribuye como un paquete ESM-first. Importas desde build/pdf.mjs y cargas un worker separado (pdf.worker.mjs) para mantener el renderizado fuera del hilo principal.

npm install pdfjs-dist
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import * as pdfjsLib from 'pdfjs-dist/build/pdf.mjs'

pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
  'pdfjs-dist/build/pdf.worker.mjs',
  import.meta.url
).toString()

const canvasRef = ref<HTMLCanvasElement | null>(null)

onMounted(async () => {
  const pdf = await pdfjsLib.getDocument('/documents/report.pdf').promise
  const page = await pdf.getPage(1)
  const viewport = page.getViewport({ scale: 1.5 })
  const canvas = canvasRef.value!
  const ctx = canvas.getContext('2d')!
  canvas.height = viewport.height
  canvas.width = viewport.width
  await page.render({ canvasContext: ctx, viewport }).promise
})
</script>

<template>
  <canvas ref="canvasRef" />
</template>

Lo que ganas: Control total sobre el renderizado, navegación de páginas, zoom y tematización. PDF.js soporta anotaciones estándar y campos AcroForm, aunque el comportamiento de formularios y anotaciones puede depender de la configuración de renderizado.

Lo que debes saber:

  • El paquete pdfjs-dist añade una carga notable a tu bundle del cliente. Usa import() dinámico para cargarlo de forma diferida.
  • Los formularios basados en XFA (comunes en PDFs gubernamentales antiguos) tienen soporte limitado y pueden no renderizarse correctamente.
  • Para documentos grandes, PDF.js puede obtener PDFs en fragmentos cuando el servidor soporta solicitudes de rango HTTP (Accept-Ranges: bytes), lo que puede mejorar el rendimiento percibido para archivos grandes.
  • La configuración del worker es el problema de configuración más común en proyectos Vite. El patrón new URL(..., import.meta.url) mostrado arriba resuelve la ruta del worker correctamente en tiempo de compilación.

3. Wrappers de Vue PDF

Varios componentes de Vue 3 mantenidos envuelven PDF.js y exponen una API de componente más simple. vue-pdf-embed es una opción mantenida activamente que maneja la configuración del worker, el renderizado de páginas y las actualizaciones de props reactivas por ti.

<script setup lang="ts">
import VuePdfEmbed from 'vue-pdf-embed'
</script>

<template>
  <VuePdfEmbed source="/documents/report.pdf" />
</template>

El compromiso es menos control granular a cambio de significativamente menos código repetitivo. Estos wrappers son una buena opción cuando necesitas un visor de PDF de Vue 3 funcionando rápidamente y no necesitas personalizar el pipeline de renderizado.

Nota: Algunos paquetes más antiguos como vue3-pdf-app ya no se mantienen activamente. Evalúa el estado de mantenimiento y compatibilidad de cualquier wrapper con bundlers modernos como Vite antes de adoptarlo.


Eligiendo el Enfoque Correcto

EnfoqueCosto de BundlePersonalizaciónMejor Para
<iframe> / <embed>0 KBNingunaIncrustaciones rápidas, archivos mismo origen
PDF.js directoCarga del lado del clienteCompletaVisores personalizados, documentos grandes
Wrapper de VueSimilar a PDF.jsModeradaConfiguración más rápida, casos de uso estándar

Conclusión

Para la mayoría de las aplicaciones Vue 3, la decisión es directa: usa <iframe> para incrustaciones simples del mismo origen, recurre a un wrapper de Vue mantenido cuando necesites un visor funcionando sin mucha configuración, e integra PDF.js directamente cuando necesites control total sobre el renderizado, navegación o rendimiento para documentos grandes. Cualquiera que sea el camino que elijas, configura el worker de PDF.js correctamente y carga la librería de forma diferida. Solo esos dos pasos prevendrán los problemas más comunes que los desarrolladores encuentran al incrustar PDFs en aplicaciones Vue.

Preguntas Frecuentes

El navegador bloquea solicitudes de PDF de origen cruzado a menos que el servidor incluya el encabezado Access-Control-Allow-Origin en su respuesta. La solución más confiable es configurar un endpoint proxy del mismo origen en tu propio backend que obtenga el PDF y lo sirva a tu aplicación Vue. Esto evita depender de configuraciones de servidores de terceros que podrías no controlar.

Vite maneja las URLs de assets de manera diferente a Webpack. Usa el patrón new URL con import.meta.url para resolver la ruta del worker en tiempo de compilación. Por ejemplo, establece workerSrc a new URL de pdfjs-dist/build/pdf.worker.mjs con import.meta.url, luego llama a toString. Esto asegura que Vite procese el archivo del worker correctamente durante las compilaciones de desarrollo y producción.

Sí. Después de cargar el documento, itera desde 1 hasta pdf.numPages, llama a getPage para cada número de página, crea un elemento canvas para cada una, y renderízalas secuencialmente o en paralelo. Para documentos muy grandes, considera renderizar solo las páginas visibles y cargar otras al hacer scroll para evitar alto uso de memoria y tiempos de renderizado inicial lentos.

Usa un wrapper como vue-pdf-embed si necesitas un visor estándar con configuración mínima y no requieres personalización profunda. Integra PDF.js directamente cuando necesites control total sobre el renderizado, navegación personalizada, tematización u optimizaciones de rendimiento como carga diferida de páginas. Los wrappers añaden una capa delgada sobre PDF.js, por lo que el costo del bundle es casi idéntico de cualquier manera.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay