Documentation
Dashboard

PDF Export

The monthly PDF mirrors the Client Portal. Here's the page order, where each page lives, and how to add a new one.

How it triggers

The PDF Download button is a client-only component (PDFDownloadButton.tsx) that uses @react-pdf/renderer's PDFDownloadLink. It re-renders the entire report into a PDF blob in the browser when clicked.

We render it via next/dynamic with ssr: false because @react-pdf/renderer uses browser APIs that don't exist in Node:

const PDFDownloadButton = dynamic(
  () => import('@/components/reports/PDFDownloadButton'),
  { ssr: false }
)

Page order (locked)

#PageSource
1CoverClient name + screenshot + date range
2SummarySame data as Portal Summary tab
3Historical RevenueRFMS sheet
4Snapshot & Goalsportal_data.snapshot
5Scorecardportal_data.scorecard
690-Day Game Planportal_data.gameplan
7Journey & Deliverablesportal_data.deliverables
8KPI Reportingportal_data.kpi_reporting
9Content & Websiteportal_data.content_tracker
Mirror the portal, deliberately
Page order intentionally matches the Portal sub-tab order so an admin reviewing the dashboard with a client can flip through the PDF in the same sequence.

Component layout

  • src/components/reports/MonthlyReportPDF.tsx — the full @react-pdf/renderer document. Each page is a <Page> with React-PDF primitives (View, Text, Image, Svg).
  • src/components/reports/PDFDownloadButton.tsx — the trigger button.
  • CLIENT_NAV_ITEMS constant inside MonthlyReportPDF.tsx — the sidebar nav rendered on every page.
  • GaugeCircle — SVG circular gauge component used for PageSpeed scores. Custom centering math because React-PDF's text positioning is finicky.

Props the PDF needs

MonthlyReportPDF receives every metric the dashboard has: GHL metrics, GA4 metrics, GSC metrics, Google Ads, Meta Ads, GBP, GBP reviews, YouTube, PageSpeed, RFMS, plus all six editable portal sections, plus client info, plus the date range.

These are gathered in ClientDashboard.tsx from existing state and passed in one prop bag. There is intentionally no separate fetch — if the dashboard cannot show a metric, the PDF cannot either.

Styling

React-PDF uses its own style system (StyleSheet.create) — Tailwind classes do not work. Colors, font sizes, spacing all come from the C palette and F font scale at the top of MonthlyReportPDF.tsx. Match those, do not introduce new constants per page.

Adding a new PDF page

  1. Add the entry to CLIENT_NAV_ITEMS in the desired position.
  2. Author a new page section function returning a <Page>.
  3. Insert it into the <Document> tree in the right order.
  4. Pass any new data through the props bag from ClientDashboard.tsx.
Preview before shipping
React-PDF rendering is unforgiving — a slightly off margin can push content onto a fresh page or cut text. After every change, download the PDF and review every page. There is no auto-fit.