Client-side rendering support for pdfn - enables Recharts and other client components in PDFs
npm install @pdfn/clientClient-side rendering support for pdfn templates with React hooks.
@pdfn/client enables PDF generation from templates that use React hooks (like Recharts, interactive components). Unlike server-side rendering with renderToStaticMarkup, client rendering executes hooks by running React in the browser via Puppeteer.
Use @pdfn/client when your templates contain:
- Charting libraries — Recharts, Victory, Chart.js with React wrappers
- Components with hooks — useState, useEffect, useRef
- Animation libraries — Framer Motion, React Spring
- Any "use client" components
``
Standard SSR (hooks skipped):
┌─────────────────────────────────────────────┐
│ renderToStaticMarkup(Template) │
│ └── Recharts hooks skipped → empty divs │
└─────────────────────────────────────────────┘
Client rendering (hooks execute):
┌─────────────────────────────────────────────┐
│ 1. Detect "use client" in template │
│ 2. Bundle template with esbuild │
│ 3. Generate HTML with embedded bundle │
│ 4. Puppeteer loads HTML → React renders │
│ (hooks execute, charts appear) │
│ 5. Paged.js paginates → PDF │
└─────────────────────────────────────────────┘
`
@pdfn/client is included automatically when you install @pdfn/next or @pdfn/vite. You don't need to install it separately.
When using @pdfn/next or @pdfn/vite, client components are detected automatically via the "use client" directive:
`tsx
// pdfn-templates/report.tsx
"use client";
import { BarChart, Bar, XAxis, YAxis } from 'recharts';
export default function Report({ data }) {
return (
);
}
`
`typescript
import { bundleForClient, generateClientHtml, hasClientMarkers } from '@pdfn/client';
// Check if template needs client rendering
if (hasClientMarkers(element)) {
// Bundle template for browser
const bundle = await bundleForClient({
templatePath: '/path/to/template.tsx',
props: { data: [...] },
cwd: process.cwd(),
});
// Generate HTML with embedded bundle
const html = generateClientHtml({
bundleCode: bundle,
title: 'Report',
css: compiledTailwindCss,
});
}
`
For production, templates are pre-bundled at build time:
`typescript
// next.config.ts
import { withPdfn } from '@pdfn/next';
export default withPdfn({
// Your Next.js config
});
`
`typescript
// vite.config.ts
import { pdfn } from '@pdfn/vite';
export default {
plugins: [pdfn()],
};
`
Bundles a template for client-side rendering.
`typescript`
const bundle = await bundleForClient({
templatePath: string, // Absolute path to template
props?: object, // Props to pass to template
cwd?: string, // Working directory
debug?: boolean, // Enable debug logging
});
Generates HTML that loads and renders the bundled template.
`typescript`
const html = generateClientHtml({
bundleCode: string, // Output from bundleForClient
title?: string, // Document title
css?: string, // CSS to include
pageSize?: string, // e.g., "A4"
orientation?: string, // "portrait" or "landscape"
});
Checks if a React element tree contains client component markers.
`typescript`
const needsClientRendering = hasClientMarkers(
- react ^18.0.0 || ^19.0.0react-dom` ^18.0.0 || ^19.0.0
-
MIT