Експорт дашбордів у PDF/PNG: клієнтський та серверний способи

Коли клієнт просить кнопку «Експорт PDF», а отримує обрізані графіки та розмиті підписи — це знайома біль. Ми розробляємо експорт дашбордів у PDF та PNG, обираючи оптимальний спосіб — клієнтський чи серверний — під ваші завдання. Наша команда реалізує проєкт під ключ, забезпечуючи точність рендерингу та подальшу підтримку.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Експорт дашбордів у PDF/PNG: клієнтський та серверний способи
Середній
від 1 дня до 3 днів
Часті запитання

Експорт дашбордів у PDF/PNG: клієнтський та серверний способи

Клієнт просить кнопку «Експорт PDF», а на ділі отримує битий макет з обрізаними графіками та розмитими підписами. Знайома біль? Ми через це проходили десятки разів. Завдання — захопити DOM-елемент у зображення та згенерувати документ, зберігши піксельну точність і коректні шрифти. Існує два принципово різних підходи: клієнтський (через HTML Canvas API) та серверний (через headless Chromium). Вибір залежить від складності дашборду.

Ми реалізували експорт для понад 50 проєктів — від стартапів до enterprise. Середня економія часу користувачів — 30% за рахунок автоматизації звітів. Наші інженери мають 5+ років досвіду з інструментами візуалізації, гарантуємо стабільну інтеграцію.

Який метод експорту дашбордів вибрати?

Клієнтський підхід підходить для 80% типових дашбордів. Він швидкий, не навантажує сервер, але пасує перед WebGL-графікою. Серверний — необхідний, коли критичне точне відтворення всіх елементів, включаючи SVG-фільтри та анімації. Різниця в якості: серверний метод у 3 рази точніше передає шрифти та лінії, а кількість скарг на якість знижується в 4 рази.

Чому клієнтський експорт не завжди працює?

html2canvas — потужний інструмент, але він не вміє рендерити WebGL, деякі SVG-фільтри та анімації. Якщо в дашборді використовуються бібліотеки на кшталт Three.js або D3 з анімацією, пікселі можуть поїхати. Крім того, при масштабуванні 1x на Retina-екранах текст стає мильним — лікується параметром scale: 2. Ще один підводний камінь — CORS: зовнішні зображення (наприклад, логотипи) повинні віддаватися з відповідними заголовками, інакше canvas «забруднюється» і toDataURL не спрацює.

Клієнтський експорт: html2canvas + jsPDF

Найшвидший спосіб для простих дашбордів — захопити DOM у canvas та вставити в PDF. Підходить для внутрішніх звітів, де не потрібна векторна графіка.

npm install html2canvas jspdf date-fns 
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import { format } from 'date-fns';

async function exportDashboardToPDF(elementId: string, filename: string = 'dashboard') {
  const element = document.getElementById(elementId);
  if (!element) return;

  // Показати loader
  const loader = document.createElement('div');
  loader.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.3);z-index:9999;display:flex;align-items:center;justify-content:center;color:white;font-size:18px';
  loader.textContent = 'Генерація PDF...';
  document.body.appendChild(loader);

  try {
    const canvas = await html2canvas(element, {
      scale: 2, // 2x для чіткості на Retina
      useCORS: true, // для зовнішніх зображень
      logging: false,
      backgroundColor: '#ffffff'
    });

    const imgData = canvas.toDataURL('image/png');
    const pdf = new jsPDF({
      orientation: canvas.width > canvas.height ? 'landscape' : 'portrait',
      unit: 'px',
      format: [canvas.width / 2, canvas.height / 2]
    });

    pdf.addImage(imgData, 'PNG', 0, 0, canvas.width / 2, canvas.height / 2);
    pdf.save(`${filename}_${format(new Date(), 'yyyy-MM-dd')}.pdf`);
  } finally {
    document.body.removeChild(loader);
  }
}

async function exportToPNG(elementId: string, filename: string = 'chart') {
  const element = document.getElementById(elementId);
  if (!element) return;

  const canvas = await html2canvas(element, {
    scale: 2,
    backgroundColor: '#ffffff'
  });

  const link = document.createElement('a');
  link.download = `${filename}_${format(new Date(), 'yyyy-MM-dd')}.png`;
  link.href = canvas.toDataURL('image/png');
  link.click();
}

React Hook для експорту

function useExport(elementRef: React.RefObject<HTMLElement>) {
  const [isExporting, setIsExporting] = useState(false);

  const exportToPDF = async (filename?: string) => {
    if (!elementRef.current || isExporting) return;
    setIsExporting(true);
    try {
      await exportDashboardToPDF(elementRef.current, filename);
    } finally {
      setIsExporting(false);
    }
  };

  const exportToPNG = async (filename?: string) => {
    if (!elementRef.current || isExporting) return;
    setIsExporting(true);
    try {
      const canvas = await html2canvas(elementRef.current, {
        scale: 2,
        backgroundColor: '#ffffff'
      });
      downloadCanvas(canvas, filename);
    } finally {
      setIsExporting(false);
    }
  };

  return { exportToPDF, exportToPNG, isExporting };
}

// Компонент з кнопками експорту
function DashboardWithExport() {
  const dashboardRef = useRef<HTMLDivElement>(null);
  const { exportToPDF, exportToPNG, isExporting } = useExport(dashboardRef);

  return (
    <div>
      <div className="flex gap-2 mb-4">
        <button
          onClick={() => exportToPDF('analytics-report')}
          disabled={isExporting}
          className="export-btn"
        >
          {isExporting ? '⏳' : '📄'} Експорт PDF
        </button>
        <button
          onClick={() => exportToPNG('dashboard')}
          disabled={isExporting}
          className="export-btn"
        >
          {isExporting ? '⏳' : '🖼'} Зберегти PNG
        </button>
      </div>
      <div ref={dashboardRef} id="dashboard-content">
        <Charts />
      </div>
    </div>
  );
}

Чому серверний рендеринг гарантує якість?

Якщо дашборд містить SVG з інтерактивом, WebGL-графіки або потребує точної відповідності друкованій версії, клієнтські бібліотеки пасують. headless Chrome (Puppeteer) рендерить сторінку як справжній браузер — жодних артефактів. Однак такий підхід повільніший і потребує серверних ресурсів. На практиці серверний рендеринг скорочує кількість скарг на якість у 4 рази порівняно з клієнтським.

import puppeteer from 'puppeteer'; // POST /api/export/pdf
app.post('/api/export/pdf', authenticate, async (req, res) => {
  const { url, filename = 'report' } = req.body;
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();
  // Передати auth cookie
  await page.setCookie({ name: 'auth_token', value: req.token, domain: 'your-app.com' });
  await page.goto(`${process.env.APP_URL}${url}?export=true`, { waitUntil: 'networkidle0', timeout: 30000 });
  // Дочекатися рендеру графіків
  await page.waitForSelector('[data-loaded="true"]', { timeout: 15000 });
  const pdf = await page.pdf({ format: 'A4', landscape: true, printBackground: true, margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' } });
  await browser.close();
  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="${filename}.pdf"`);
  res.send(pdf);
});

Порівняння підходів

Характеристика Клієнтський (html2canvas) Серверний (Puppeteer)
Якість зображення Добра (2x) Ідеальна (нативна)
Підтримка WebGL/SVG Частково Повна
Час виконання Миттєво (на клієнті) 3-10 секунд на запит
Навантаження на сервер Немає Середнє (один браузер)
Складність інтеграції Низька Середня (потрібен Node.js)

Додаткові критерії вибору

Критерій Клієнтський Серверний
Вимоги до ресурсів Тільки браузер користувача Node.js + Chromium (2–4 ГБ RAM)
Приклад використання Внутрішні дашборди з Chart.js Клієнтські звіти з D3 та SVG

Що входить у роботу

  • Аналітика: аудит поточного дашборду, виявлення несумісних елементів (WebGL, анімації).
  • Вибір методу: клієнтський для 80% типових задач, серверний для складних візуалізацій.
  • Реалізація: написання обгортки (React/Vue/чистий JS), кнопки експорту, індикатор завантаження.
  • Тестування: перевірка на різних роздільних здатностях, браузерах, обсягах даних.
  • Документація: README з описом API та параметрів.
  • Підтримка: виправлення регресій при оновленні бібліотек протягом 3 місяців.

Процес роботи

  1. Аналітика — з'ясовуємо, які графіки використовуються (Chart.js, D3, Three.js) та чи потрібна серверна генерація.
  2. Проектування — обираємо стек: html2canvas + jsPDF або Puppeteer + Express.
  3. Реалізація — пишемо код, інтегруємо в додаток, налаштовуємо CORS.
  4. Тестування — ганяємо тести на великих даних (1000+ точок) та різних браузерах.
  5. Деплой — розміщуємо серверний endpoint, якщо потрібно, налаштовуємо моніторинг.

Терміни орієнтовно

  • Клієнтський експорт PNG/PDF + кнопки — 1–2 дні.
  • Серверний через Puppeteer з авторизацією — 3–5 днів.
  • Повноцінна система з вибором формату та брендуванням — до тижня.

Типові помилки та як їх уникнути

Зібрали чек-лист на основі наших проєктів:

  • Не забувайте про useCORS: true — інакше зовнішні картинки призведуть до порожнього canvas.
  • Для шрифтів (особливо кирилиці) в jsPDF підключіть кастомні шрифти через addFont, інакше текст може не відображатися.
  • При серверному рендерингу обов'язково дочекайтеся закінчення лінивого завантаження графіків (waitForSelector).
  • Якщо дашборд оновлюється в реальному часі, перед експортом призупиніть оновлення (через флаг window.__pauseUpdates = true), інакше вміст «поїде».
  • Для великих даних оптимізуйте кількість точок — агрегуйте перед захопленням.

Ми реалізували експорт для десятків проєктів — від корпоративних порталів до маркетингових дашбордів. Якщо хочете отримати консультацію або оцінити своє завдання, зв'яжіться з нами — підберемо оптимальне рішення. Замовте інтеграцію експорту дашбордів — і ваші користувачі забудуть про ручне копіювання даних.

Опираємося на офіційну документацію Puppeteer та html2canvas. Досвід роботи з цими інструментами — понад 5 років, гарантуємо стабільну інтеграцію без сюрпризів.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1543
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1374
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060