@spexzee/react-pdfhook
Version:
React hook for generating PDF documents from components
257 lines (209 loc) • 6.65 kB
Markdown
# React PDF Generator Hook - @spexzee/react-pdfhook




A production-ready React hook for generating high-fidelity PDFs with advanced layout control and content targeting.
Main Issue solved : if data doesn't fit in a remaining screen it will start from next page
## Features
- **Precision PDF Generation**
- Convert React components to pixel-perfect PDFs
- Support for all HTML/CSS that html2canvas can render
- Automatic and manual page break controls
- **Advanced Content Targeting**
- Flexible selector system for specific elements
- PDF-only content visibility (show in PDF but hide in browser)
- Image handling with CORS support
- **Layout Control**
- Fixed-width mode for consistent cross-device rendering
- Responsive design preservation
- Customizable margins and page formats
- **Optimized Performance**
- Lightweight (~5kB gzipped)
- Zero runtime dependencies
- TypeScript-first design
## Installation
```bash
npm install @spexzee/react-pdfhook jspdf html2canvas
# or
yarn add @spexzee/react-pdfhook jspdf html2canvas
```
## Basic Usage
```jsx
import { usePdfGenerator } from '@spexzee/react-pdfhook';
function DocumentGenerator() {
const { generatePdf, pdfRef } = usePdfGenerator({
fileName: 'document.pdf'
});
const handleDownload = async () => {
await generatePdf([
{
selector: '/images/header.jpg',
mapping: false,
type: 'image',
imageOptions: {
width: 180, // mm
maintainAspectRatio: true
}
},
{
selector: '.header-content',
mapping: false,
type: 'element'
},
{
selector: '.multi-data',
// advantage of using mapping , it will single single data into pdf , so we can easilt manage page-breaks ,
// if data doesn't fit in a remaining screen it will start from next page
mapping: true,
type: 'element'
}
]);
};
return (
<div>
<button onClick={handleDownload}>Generate PDF</button>
<div ref={pdfRef}>
<div className="header-content">
<h1>My Document</h1>
<p>This content will appear in the PDF</p>
</div>
{
data.map((x)=>(
<div className="multi-data">
<AnyComponentName data={x}>
</div>
))
}
</div>
</div>
);
}
```
### 2. PDF-Only Content
```jsx
<div ref={pdfRef}>
{/* Visible in both browser and PDF */}
<h1>Public Report</h1>
{/* Hidden in browser, visible only in PDF */}
<div className="pdf-only">
<h2>Confidential Details</h2>
<p>Only visible in the generated PDF</p>
</div>
</div>
```
### 2. Screen-Only Content
```jsx
<div ref={pdfRef}>
{/* Visible in both browser and PDF */}
<h1>Public Report</h1>
{/* Hidden in pdf, visible only in browser */}
<div className="screen-only">
<h2>Confidential Details</h2>
<p>Only visible in the generated PDF</p>
</div>
</div>
```
**Required CSS for PDF Only:**
```css
.pdf-only {
display: none;
}
@media print {
.pdf-only {
display: block;
}
}
```
### 3. Layout Control
```jsx
const { generatePdf } = usePdfGenerator({
fixedWidth: 1200, // pixels (optimal for A4 width)
scale: 1.5, // Quality/performance balance
margin: { top: 20, right: 15, bottom: 20, left: 15 }, // mm
pageBreak: {
before: true,
avoid: ['.keep-together'] // CSS selector of elements to keep together
}
});
```
### 4. Image Handling
```jsx
// Local images (must be in public folder)
<img src="/images/logo.png" alt="Logo" />
// Remote images (require CORS)
<img
src="https://example.com/chart.png"
crossOrigin="anonymous"
alt="Chart"
/>
```
## Complete API Reference
### Hook Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `fileName` | string | 'document.pdf' | Output filename |
| `format` | string/array | 'a4' | Page size (a3,a4,letter or [w,h] in mm) |
| `orientation` | string | 'portrait' | 'portrait' or 'landscape' |
| `margin` | number/object | 0 | Margins in mm |
| `fixedWidth` | number | undefined | Constrained width in pixels |
| `scale` | number | 2 | Render quality multiplier |
| `pageBreak` | boolean/object | true | Auto page break config |
| `imageQuality` | number | 1 | Image quality (0-1) |
| `debug` | boolean | false | Enable debug logging |
### Content Configuration
```typescript
interface PdfContentItem {
selector: string; // CSS selector or image path
mapping: boolean; // true = all matches, false = first match
type: 'element'|'image';
imageOptions?: { // Only for type: 'image'
width?: number; // mm
height?: number; // mm
x?: number; // mm
y?: number; // mm
format?: 'JPEG'|'PNG';
maintainAspectRatio?: boolean;
};
}
```
## Best Practices
1. **For images**:
- Place assets in `public` folder
- Use absolute paths (`/images/photo.jpg`)
- Set explicit dimensions in `imageOptions`
2. **For performance**:
```jsx
{
scale: 1, // Lower quality but faster
imageQuality: 0.8,
fixedWidth: 800 // Smaller fixed width
}
```
3. **For complex layouts**:
```jsx
// Add to elements that should stay together
<div className="keep-together">
<h2>Section Title</h2>
<p>Content that shouldn't be split across pages</p>
</div>
```
## Troubleshooting
**Issue** | **Solution**
---|---
Images missing | Verify paths and CORS headers
Content clipped | Increase margins or reduce fixedWidth
PDF-only content not showing | Check CSS and media queries
Performance problems | Reduce scale and imageQuality
## Changelog
**v1.0.0** (Current)
- Added advanced content targeting
- Improved image handling
- Enhanced TypeScript support
- Better layout preservation
## License
MIT © [Spexzee](https://github.com/spexzee)
---
**Like this package?** ⭐️ [Star the repo](https://github.com/spexzee/react-pdfhook)
**Need help?** 📧 [Contact me](mailto:spexzee.abufxu69@gmail.com)
**Found a bug?** 🐛 [Open an issue](https://github.com/spexzee/react-pdfhook/issues)