react-acroform
v1.0.1
Published
React component for rendering PDFs with full AcroForm support and bi-directional data binding
Maintainers
Readme
react-acroform
A React component library for rendering PDFs with full AcroForm support, enabling bi-directional data binding between PDF form fields and React state.
Features
- Render PDFs using Mozilla's PDF.js
- Full AcroForm support (text fields, checkboxes, radio buttons, dropdowns, signatures)
- Bi-directional data binding between PDF fields and React state
- Page navigation with thumbnail sidebar
- Zoom controls
- PDF export with filled form data
- Field name aliasing for friendly API
- TypeScript support with full type definitions
Installation
npm install react-acroform pdfjs-distBasic Usage
import { PDFViewer } from 'react-acroform';
function App() {
const [formData, setFormData] = useState({});
const handleChange = (fieldName, value, allValues) => {
setFormData(allValues);
console.log(`Field "${fieldName}" changed to:`, value);
};
return (
<PDFViewer
src="/path/to/document.pdf"
values={formData}
onChange={handleChange}
showNavigation={true}
showThumbnails={true}
scale={1}
/>
);
}Props
PDFViewer
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| src | string \| ArrayBuffer \| Uint8Array | required | PDF source (URL, ArrayBuffer, or Uint8Array) |
| values | Record<string, FormFieldValue> | - | Controlled form values (inbound) |
| defaultValues | Record<string, FormFieldValue> | - | Default values for fields |
| onChange | (fieldName, value, allValues) => void | - | Callback when a field value changes (outbound) |
| fieldNameMap | Record<string, string> | - | Map friendly names to PDF field names |
| onDocumentLoad | (doc: PDFDocumentInfo) => void | - | Callback when document loads |
| onPageChange | (page: number) => void | - | Callback when page changes |
| showNavigation | boolean | true | Show navigation controls |
| showThumbnails | boolean | false | Show thumbnail sidebar |
| scale | number \| 'auto' \| 'page-fit' \| 'page-width' | 1 | Zoom scale |
| initialPage | number | 1 | Initial page to display |
| className | string | - | Container class name |
| pageClassName | string | - | CSS class for page elements |
| thumbnailClassName | string | - | CSS class for thumbnail elements |
| workerSrc | string | CDN URL | PDF.js worker source URL |
Types
FormFieldValue
type FormFieldValue = string | boolean | string[];FormField
interface FormField {
name: string;
type: 'text' | 'checkbox' | 'radio' | 'dropdown' | 'signature';
value: FormFieldValue;
options?: string[]; // For dropdown/radio
required?: boolean;
readOnly?: boolean;
rect: [number, number, number, number]; // Position [x1, y1, x2, y2]
page: number;
maxLength?: number;
multiline?: boolean;
fontSize?: number;
fontColor?: string;
backgroundColor?: string;
borderColor?: string;
}PDFDocumentInfo
interface PDFDocumentInfo {
numPages: number;
fields: FormField[];
metadata: Record<string, string>;
document?: PDFDocumentProxy; // PDF.js document proxy for advanced operations
}Hooks
usePDFDocument
Load and manage a PDF document.
import { usePDFDocument } from 'react-acroform';
const { document, loading, error, numPages, reload } = usePDFDocument({
src: '/path/to/document.pdf',
workerSrc: '/pdf.worker.min.js', // optional
});useFormFields
Extract form fields from a PDF document.
import { useFormFields } from 'react-acroform';
const {
fields,
loading,
error,
getFieldsForPage,
getFieldByName,
getInitialFormData,
radioGroups,
} = useFormFields({
document,
onFieldsExtracted: (fields) => console.log('Found fields:', fields),
});useFormState
Bi-directional sync between form fields and external state.
import { useFormState } from 'react-acroform';
const {
getValue,
setValue,
getAllValues,
initializeFromPDF,
resolveFieldName,
reverseResolveFieldName,
version,
} = useFormState({
values: externalValues,
defaultValues: { firstName: 'John' },
onChange: (name, value, allValues) => console.log(name, value),
fieldNameMap: { firstName: 'form1[0].firstName[0]' },
});usePDFExport
Export PDFs with filled form data.
import { usePDFExport } from 'react-acroform';
const { exportPDF, downloading, error } = usePDFExport();usePageRenderer
Render PDF pages to canvas.
import { usePageRenderer } from 'react-acroform';
const { canvasRef, rendering, error } = usePageRenderer({
page,
scale: 1.5,
});Custom Field Components
You can use the individual field components for custom layouts:
import {
TextField,
CheckboxField,
RadioField,
DropdownField,
SignatureField,
BaseField,
} from 'react-acroform';
<TextField
field={field}
scale={1}
pageHeight={792}
/>
<SignatureField
field={field}
scale={1}
pageHeight={792}
onSignatureClick={(field) => console.log('Sign:', field.name)}
/>Additional Components
import {
PDFPage,
PDFNavigation,
PDFThumbnails,
FormStateProvider,
} from 'react-acroform';
// FormStateProvider wraps components that need form state context
<FormStateProvider
values={formData}
onChange={handleChange}
fieldNameMap={fieldNameMap}
>
<YourCustomViewer />
</FormStateProvider>Utilities
Coordinate Transformation
import { pdfRectToScreen, getFieldPositionStyle, calculateFitScale } from 'react-acroform';
// Convert PDF coordinates to screen coordinates
const screenRect = pdfRectToScreen(pdfRect, pageHeight, scale);
// Get CSS position style for a field
const style = getFieldPositionStyle(field.rect, pageHeight, scale);
// Calculate scale to fit page in container
const scale = calculateFitScale(pageWidth, pageHeight, containerWidth, containerHeight, 'fit');Field Extraction
import { extractAllFields, fieldsToFormData } from 'react-acroform';
// Extract all fields from a PDF document
const fields = await extractAllFields(document);
// Convert fields to form data object
const initialData = fieldsToFormData(fields);Field Mapping
import { groupFieldsByName, getFieldsForPage, updateFormData } from 'react-acroform';
// Group fields by name (useful for radio buttons)
const groups = groupFieldsByName(fields);
// Get fields for a specific page
const pageFields = getFieldsForPage(fields, 1);PDF Export
import { fillPDF, downloadPDF, fillAndDownloadPDF } from 'react-acroform';
// Fill a PDF with form data and get bytes
const { pdfBytes, fieldCount } = await fillPDF(pdfSource, formData);
// Download a filled PDF
await downloadPDF(pdfBytes, 'filled-form.pdf');
// Fill and download in one step
await fillAndDownloadPDF(pdfSource, formData, 'filled-form.pdf');Canvas Export
import { exportPDFFromCanvas, downloadCanvasExportedPDF } from 'react-acroform';
// Export PDF pages as images in a new PDF
const result = await exportPDFFromCanvas(canvasElements);
// Download the exported PDF
await downloadCanvasExportedPDF(canvasElements, 'exported.pdf');Field Name Mapping
import { createFieldNameMapper } from 'react-acroform';
// Create a mapper for friendly field names
const mapper = createFieldNameMapper({
firstName: 'form1[0].firstName[0]',
lastName: 'form1[0].lastName[0]',
});
const pdfName = mapper.resolve('firstName'); // 'form1[0].firstName[0]'
const friendlyName = mapper.reverseResolve('form1[0].firstName[0]'); // 'firstName'PDF.js Worker
For production use, you should host the PDF.js worker yourself:
<PDFViewer
src={pdfUrl}
workerSrc="/pdf.worker.min.js"
/>Copy node_modules/pdfjs-dist/build/pdf.worker.min.js to your public directory.
License
MIT
