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)
| # | Page | Source |
|---|---|---|
| 1 | Cover | Client name + screenshot + date range |
| 2 | Summary | Same data as Portal Summary tab |
| 3 | Historical Revenue | RFMS sheet |
| 4 | Snapshot & Goals | portal_data.snapshot |
| 5 | Scorecard | portal_data.scorecard |
| 6 | 90-Day Game Plan | portal_data.gameplan |
| 7 | Journey & Deliverables | portal_data.deliverables |
| 8 | KPI Reporting | portal_data.kpi_reporting |
| 9 | Content & Website | portal_data.content_tracker |
Component layout
src/components/reports/MonthlyReportPDF.tsx— the full@react-pdf/rendererdocument. Each page is a<Page>with React-PDF primitives (View,Text,Image,Svg).src/components/reports/PDFDownloadButton.tsx— the trigger button.CLIENT_NAV_ITEMSconstant insideMonthlyReportPDF.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
- Add the entry to
CLIENT_NAV_ITEMSin the desired position. - Author a new page section function returning a
<Page>. - Insert it into the
<Document>tree in the right order. - Pass any new data through the props bag from
ClientDashboard.tsx.