main content
< Volver a blog sobre aplicaciones móviles

Exportar informes PDF y Excel en tu app a medida

Tus usuarios quieren descargar facturas y cuadros de mando: cómo implementar exportación de informes PDF y Excel en una aplicación web a medida sin que se te caiga nada

Hay una frase que aparece en casi todas las demos de producto: "esto está muy bien, pero ¿lo puedo bajar en Excel?". Y poco después llega la otra: "¿y la factura me la puedes dar en PDF para enviarla al cliente?". Da igual lo bonito que sea tu panel: si el usuario no puede sacar los datos en el formato que ya usa para trabajar, percibe la herramienta como incompleta.

La buena noticia es que la exportación de datos es una de esas funciones que aportan muchísimo valor percibido con un coste razonable, si se diseña bien desde el principio. La mala es que es fácil chapuzar: un PDF que tarda 40 segundos, un "Excel" que en realidad es un CSV feo, o una descarga que tumba el servidor cuando alguien pide 200.000 filas. Vamos a verlo paso a paso, con criterio de producto y sin perdernos en la teoría.

Para qué exporta de verdad la gente (y por qué importa elegir formato)

Antes de tocar una librería, conviene tener claro el caso de uso real, porque PDF y Excel no compiten: resuelven necesidades distintas.

  • Documentos para enviar o archivar → PDF. Facturas, presupuestos, contratos, certificados, informes de gestión que va a leer un humano (un comité, un cliente, una auditoría). Lo importante aquí es el aspecto: branding, maquetación fija, que se imprima igual en cualquier sitio.
  • Datos para volver a trabajar → Excel. Listados de pedidos, conciliaciones, exportaciones contables, cuadros que el usuario quiere filtrar, sumar o pegar en su propio modelo. Aquí lo que manda es la estructura: columnas tipadas, fechas que Excel reconozca como fechas y números con los que se pueda operar.

Un error clásico es ofrecer solo PDF "porque queda más profesional". El responsable financiero que recibe un informe en PDF y necesita los datos te los va a pedir igualmente en otro formato, o peor, los va a teclear a mano. Cuando dudes, ofrece ambos y deja que cada perfil elija.

Un mapa rápido de decisiones

  1. ¿El resultado lo lee una persona tal cual? → PDF.
  2. ¿El resultado lo va a manipular alguien en una hoja de cálculo? → Excel (.xlsx de verdad).
  3. ¿Es un volcado masivo para otro sistema o un import? → CSV, que es ligero y universal.
  4. ¿Hace falta sello de "documento oficial", firma o cumplimiento (por ejemplo facturas con sus requisitos de la AEAT)? → PDF, y revisa la normativa vigente porque cambia.

Generar PDF: del HTML que ya tienes al documento final

La forma más rentable de generar PDF en una app web a medida casi nunca es maquetar a mano con primitivas gráficas. Lo lógico es reutilizar lo que ya sabes hacer: HTML y CSS.

Plantillas HTML → PDF con Chromium headless

El enfoque que mejor resultado da hoy es renderizar una plantilla HTML y "imprimirla" a PDF con un navegador headless (Chromium vía Puppeteer/Playwright, o un servicio equivalente). Ventajas concretas:

  • Reaprovechas tu stack de front: una plantilla HTML con su CSS, tus tipografías, tu logo. El equipo de diseño puede tocarla sin saber de generación de PDF.
  • Soporte real de CSS moderno: Flexbox, Grid, web fonts, saltos de página controlados con page-break, cabeceras y pies repetidos. Esto, con librerías antiguas, era un sufrimiento.
  • Una sola plantilla sirve para ver online y para exportar, así que no mantienes dos maquetaciones que se desincronizan.

El coste es que necesitas un Chromium corriendo en el servidor (más memoria y CPU) y conviene reutilizar instancias en lugar de arrancar una por petición. Alternativas más ligeras como wkhtmltopdf siguen siendo válidas para documentos sencillos y consumen menos, pero su motor es antiguo y se atraganta con CSS moderno: si tus facturas son simples, cumple; si quieres maquetación rica, te frustrará.

Librerías de bajo nivel: cuándo sí

Para documentos donde controlas cada milímetro (etiquetas, certificados con posiciones exactas, generación de miles de PDF idénticos por minuto), una librería de composición directa puede ser más eficiente y predecible que levantar un navegador. Es más trabajo de maquetar, pero gana en rendimiento y en huella de memoria. Como regla práctica: empieza por HTML→PDF y baja de nivel solo si un caso concreto lo justifica.

Excel de verdad, no un CSV disfrazado

Aquí es donde más se ven los atajos. Renombrar un CSV a .xls "funciona" hasta que el usuario abre el archivo y se encuentra el típico aviso de formato, las fechas españolas (04/06/2026) interpretadas al revés, los ceros a la izquierda de un código desaparecidos y los importes tratados como texto. Eso no es exportar a Excel: es trasladarle el problema al usuario.

Cuándo basta un CSV

El CSV tiene su sitio: es ligero, se genera en streaming sin apenas memoria y vale para volcados grandes o integraciones. Si lo ofreces, cuídalo: separador adecuado para España (Excel en configuración local suele esperar ;), BOM UTF-8 para que los acentos no se rompan, y comillas bien escapadas. Es la opción correcta cuando el destino es otro sistema o cuando el volumen es enorme.

Cuándo necesitas .xlsx real

Si el usuario va a trabajar dentro del archivo, genera .xlsx nativo con una librería de hoja de cálculo. Lo que aporta y un CSV no puede dar:

  • Tipos correctos: fechas como fechas, números como números, porcentajes como porcentajes. Así las sumas y los filtros funcionan sin retoques.
  • Estilos y formato: cabeceras en negrita, anchos de columna, formato de moneda en € con sus decimales, filas con color para totales.
  • Fórmulas reales: un SUMA() al pie de una columna, subtotales, una pestaña de resumen que se recalcula. Esto convierte un volcado en una herramienta.
  • Varias hojas: datos en una pestaña, parámetros del informe (fechas, filtros aplicados) en otra. Quien reciba el archivo dentro de seis meses sabrá qué está mirando.

La diferencia de percepción es enorme: un .xlsx bien hecho hace que tu producto parezca pensado por gente que entiende cómo trabaja de verdad un departamento financiero.

Exportaciones grandes: encola el trabajo, no bloquees al usuario

Aquí está el fallo de arquitectura más caro y el que más sorprende cuando llega a producción. Mientras pruebas con 50 filas todo va instantáneo. El día que alguien pide "el informe anual completo" y son 180.000 registros, la petición se queda esperando, el navegador da timeout, el usuario le da tres veces al botón, y cada clic dispara otra generación pesada. Resultado: servidor saturado y, encima, el usuario sin su archivo.

La regla es sencilla: si una exportación puede tardar más de unos pocos segundos, no la generes dentro del request HTTP. Conviértela en un trabajo asíncrono.

El patrón: encolar → generar → notificar

  1. El usuario pide el informe. El backend crea un job, lo mete en una cola y responde de inmediato: "Estamos preparando tu informe, te avisamos cuando esté listo".
  2. Un worker en segundo plano coge el job, genera el PDF o el .xlsx a su ritmo, y lo guarda en almacenamiento (con una URL temporal y firmada).
  3. Cuando termina, se notifica al usuario: un email, una notificación dentro de la app, o un badge de "descargas listas". El usuario baja el archivo cuando quiere.

Este patrón resuelve de golpe varios problemas: no hay timeouts, el botón nunca se queda colgado, puedes reintentar un job fallido sin que el usuario se entere, y limitas cuántas exportaciones pesadas corren a la vez para no asfixiar la máquina. Un ejemplo mínimo del flujo:

encolar_export(usuario_id, filtros, formato="xlsx")
  job = cola.push({usuario, filtros, formato, estado: "pendiente"})
  responder(202, "Preparando tu informe...")

# worker en segundo plano
procesar(job):
  job.estado = "generando"
  datos   = consultar_en_paginas(job.filtros, job.usuario)   # nunca todo en memoria
  fichero = render(formato, datos)
  url     = guardar_temporal(fichero, expira="24h")
  notificar(job.usuario, "Tu informe esta listo", url)
  job.estado = "completado"

Para informes que tardan, enseña una barra de progreso o al menos un estado ("en cola", "generando", "listo"). Esa transparencia evita el 80% de las incidencias de soporte del tipo "le doy y no pasa nada".

Streaming y memoria: el detalle que separa lo que aguanta de lo que se cae

Generar un informe grande cargando todos los registros en memoria es la receta para que el proceso reviente con un error de memoria justo cuando más gente lo usa. Dos hábitos lo evitan:

  • Lee la base de datos por páginas o con un cursor, no con un SELECT * que trae medio millón de filas de un tirón.
  • Escribe el resultado en streaming: ve volcando filas al archivo (o directo a almacenamiento) según las lees, en lugar de construir el documento entero en RAM y soltarlo al final.

El CSV es ideal para esto porque es lineal por naturaleza. Con .xlsx hay librerías con modo de escritura en streaming pensadas justo para volúmenes altos; úsalas cuando el informe sea grande. La idea de fondo: el uso de memoria debe ser estable, daría igual exportar 1.000 filas que 500.000.

Branding y plantillas reutilizables: que parezca tu producto, no un volcado

Un informe exportado es tu marca saliendo de la aplicación y llegando a un cliente, a un banco o a una auditoría. Merece la pena tratarlo como un punto de contacto, no como un subproducto técnico.

  • Centraliza el logo, los colores, la tipografía y los textos legales (pie con razón social, NIF, aviso de protección de datos) en una plantilla base de la que heredan todos los documentos.
  • Si el producto es multiempresa o multimarca, parametriza el branding por cliente. La misma maquinaria genera el informe con un logo u otro según quién lo pide.
  • Mantén una sola fuente de verdad para los textos. Cambiar la dirección fiscal en el pie de página no debería obligarte a tocar quince plantillas.

Cuidar esto tiene un retorno directo: documentos coherentes refuerzan la sensación de producto serio, y reducen el trabajo de mantenimiento cuando algo cambia.

Seguridad: lo más importante y lo que más se descuida

Una exportación es una vía estupenda para filtrar datos sin querer, y con el RGPD encima eso es un problema serio, no una anécdota. Algunos principios que no son negociables:

  • Filtra siempre por el usuario y sus permisos, en el servidor. El error grave es generar el informe a partir de un identificador o un filtro que llega del cliente sin volver a comprobar que esa persona puede ver esos datos. Si manipulando un parámetro alguien puede exportar los pedidos de otra empresa, tienes una brecha.
  • URLs de descarga firmadas y con caducidad. Si guardas el archivo en almacenamiento, que el enlace sea temporal y no adivinable. Un PDF de nóminas accesible por una URL pública y permanente es exactamente el incidente que no quieres notificar a la AEPD.
  • Minimiza los datos exportados. Saca solo lo que el caso de uso necesita. Volcar columnas con datos personales "por si acaso" multiplica el riesgo sin aportar valor.
  • Registra quién exporta qué y cuándo. Un log de exportaciones te salva en una auditoría y disuade del mal uso interno.
  • Limita el ritmo. Un único usuario no debería poder lanzar cien exportaciones masivas seguidas, ni por error ni a propósito.

En conjunto, trata cada exportación como una consulta sujeta a los mismos permisos que el resto de la aplicación. Nada de atajos "porque es solo una descarga".

Accesibilidad y rendimiento: dos remates que se notan

PDF accesibles

Un PDF no tiene por qué ser una imagen muerta. Si generas desde HTML, puedes producir un PDF con texto seleccionable, estructura y etiquetas (PDF/UA, o al menos texto real y orden de lectura coherente). Esto importa por accesibilidad para personas con lectores de pantalla, y de paso hace que el documento sea buscable y que se pueda copiar texto. Para el sector público español, además, la accesibilidad de los documentos suele ser un requisito normativo: si vendes ahí, no es opcional, y conviene verificar el nivel exigido vigente.

Rendimiento sin sorpresas

  • Cachea lo que se repite: si cien personas exportan el mismo catálogo del mismo día, genera una vez y reparte.
  • Reutiliza instancias del motor de PDF en lugar de arrancar y matar un navegador por documento.
  • Mide los tiempos reales de generación con datos de producción, no con tus 50 filas de prueba. Lo que decide si una exportación va síncrona o asíncrona es ese número, no una intuición.

Empieza por el caso que más duele

Si tuviera que resumir el orden sensato: identifica qué exportación pide más tu usuario (suele ser la factura en PDF o el listado en Excel), hazla bien —formato real, branding cuidado, permisos estrictos—, y deja preparada la arquitectura asíncrona desde el primer día aunque al principio todo quepa en un request. Añadir colas después, con la app ya en producción y los usuarios pegándole al botón, cuesta mucho más que dejar el hueco hecho.

En Tangram llevamos años metiendo exportación de informes en aplicaciones a medida y la mayoría de los problemas que nos encontramos son los mismos: el "Excel" que era un CSV, el PDF que tumbaba el servidor y la descarga que enseñaba datos de quien no debía. Si tienes entre manos una app y quieres que las exportaciones estén bien resueltas desde el diseño, cuéntanos qué necesita tu producto y le damos forma juntos.

Contacta con nosotros
Fila 1