PDFViewer

A full-featured PDF viewing component with form fields, digital signatures, zoom controls, and document submission capabilities.

Import

import { PDFViewer } from '@mims/sdk-react';

Basic Usage

<PDFViewer
  documentId="doc_abc123"
  documentUrl="https://example.com/document.pdf"
  onSubmit={(payload) => console.log('Submitted:', payload)}
/>

Props

Required Props

PropTypeDefaultDescription
documentIdrequiredstringUnique identifier for the document. Used for submission tracking.
documentUrlrequiredstringURL to the PDF document. Can be a signed URL from your backend.

Display Options

PropTypeDefaultDescription
initialScalenumber1Initial zoom level. 1 = 100%, 0.5 = 50%, 2 = 200%.
showToolbarbooleantrueShow/hide the toolbar with navigation and zoom controls.
editablebooleantrueAllow users to interact with and fill form fields.
showFieldToolsbooleantrueShow field creation tools in the toolbar (requires editable=true).
heightstring | number"100%"Height of the viewer container.
classNamestringAdditional CSS class names for the container.
styleReact.CSSPropertiesInline styles for the container.

Initial Data

PropTypeDefaultDescription
initialFieldsAnyFieldDefinition[][]Pre-defined fields to display on the document.

Event Callbacks

PropTypeDefaultDescription
onDocumentLoad(pages: PageInfo[]) => voidCalled when the PDF is successfully loaded.
onDocumentError(error: Error) => voidCalled when the PDF fails to load.
onFieldAdd(field: AnyFieldDefinition) => voidCalled when a new field is added.
onFieldUpdate(fieldId: string, updates: Partial<AnyFieldDefinition>) => voidCalled when a field position or properties change.
onFieldValueChange(fieldId: string, value: AnyFieldValue) => voidCalled when a field value changes.
onFieldRemove(fieldId: string) => voidCalled when a field is removed.
onBeforeSubmit(payload: DocumentSubmissionPayload) => boolean | Promise<boolean>Called before submission. Return false to cancel.
onSubmit(payload: DocumentSubmissionPayload) => void | Promise<void>Called after successful submission.
onSubmitError(error: Error) => voidCalled when submission fails.

Examples

Basic Document Viewer

function BasicViewer() {
  return (
    <div style={{ height: '600px' }}>
      <PDFViewer
        documentId="doc_123"
        documentUrl="/contracts/agreement.pdf"
        showToolbar={true}
        editable={false}
      />
    </div>
  );
}

With Pre-defined Fields

1const fields = [
2 {
3 id: 'name_field',
4 type: 'text' as const,
5 page: 1,
6 x: 0.1,
7 y: 0.3,
8 width: 0.3,
9 height: 0.04,
10 required: true,
11 editable: true,
12 label: 'Full Name',
13 placeholder: 'Enter your full name',
14 },
15 {
16 id: 'signature_field',
17 type: 'signature' as const,
18 page: 1,
19 x: 0.1,
20 y: 0.7,
21 width: 0.3,
22 height: 0.1,
23 required: true,
24 editable: true,
25 label: 'Signature',
26 },
27 {
28 id: 'date_field',
29 type: 'date' as const,
30 page: 1,
31 x: 0.5,
32 y: 0.7,
33 width: 0.2,
34 height: 0.04,
35 required: true,
36 editable: true,
37 label: 'Date',
38 },
39];
40
41function DocumentWithFields() {
42 return (
43 <PDFViewer
44 documentId="contract_456"
45 documentUrl="/contracts/nda.pdf"
46 initialFields={fields}
47 onFieldValueChange={(fieldId, value) => {
48 console.log(`Field ${fieldId} changed:`, value);
49 }}
50 />
51 );
52}

Full Submission Flow

1function ContractSigningPage({ documentId }: { documentId: string }) {
2 const [isSubmitting, setIsSubmitting] = useState(false);
3 const [submitSuccess, setSubmitSuccess] = useState(false);
4 const router = useRouter();
5
6 const handleBeforeSubmit = async (payload: DocumentSubmissionPayload) => {
7 // Validate all required fields are filled
8 const requiredFields = payload.fields.filter(f => f.required);
9 const missingFields = requiredFields.filter(f => !f.value);
10
11 if (missingFields.length > 0) {
12 alert('Please fill all required fields');
13 return false;
14 }
15
16 return true;
17 };
18
19 const handleSubmit = async (payload: DocumentSubmissionPayload) => {
20 setIsSubmitting(true);
21
22 // The SDK automatically sends to MIMS API
23 // You can also send to your own backend
24 await fetch('/api/contracts/complete', {
25 method: 'POST',
26 headers: { 'Content-Type': 'application/json' },
27 body: JSON.stringify({
28 documentId: payload.documentId,
29 submittedAt: payload.submittedAt,
30 }),
31 });
32
33 setSubmitSuccess(true);
34 setIsSubmitting(false);
35
36 // Redirect after 2 seconds
37 setTimeout(() => {
38 router.push('/contracts/completed');
39 }, 2000);
40 };
41
42 const handleSubmitError = (error: Error) => {
43 setIsSubmitting(false);
44 alert(`Submission failed: ${error.message}`);
45 };
46
47 if (submitSuccess) {
48 return (
49 <div className="flex flex-col items-center justify-center h-64">
50 <CheckCircle className="w-16 h-16 text-green-500" />
51 <h2 className="mt-4 text-xl font-semibold">Document Submitted!</h2>
52 <p className="text-gray-500">Redirecting...</p>
53 </div>
54 );
55 }
56
57 return (
58 <PDFViewer
59 documentId={documentId}
60 documentUrl={`/api/documents/${documentId}/download`}
61 onDocumentLoad={(pages) => {
62 console.log(`Loaded ${pages.length} pages`);
63 }}
64 onBeforeSubmit={handleBeforeSubmit}
65 onSubmit={handleSubmit}
66 onSubmitError={handleSubmitError}
67 />
68 );
69}

Field Positioning

Fields use normalized coordinates (0-1) relative to page dimensions. This ensures fields are positioned correctly regardless of zoom level or rendering size.

// Field at 10% from left, 30% from top
// Width: 30% of page, Height: 4% of page
{
  x: 0.1,
  y: 0.3,
  width: 0.3,
  height: 0.04,
}

Coordinate System

  • x: 0 = left edge, x: 1 = right edge
  • y: 0 = top edge, y: 1 = bottom edge
  • Width and height are also normalized (0-1)

Toolbar Features

The default toolbar includes:

  • Page Navigation: Previous/next page, page number input
  • Zoom Controls: Zoom in, zoom out, fit to width/page
  • Field Tools: Add text, signature, checkbox, date, initials fields
  • Submit Button: Validates and submits the document

Styling

Customize the viewer appearance:

<PDFViewer
  documentId="..."
  documentUrl="..."
  className="my-custom-viewer"
  style={{
    borderRadius: '8px',
    boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
  }}
/>

/* CSS */
.my-custom-viewer {
  background: #f5f5f5;
}

.my-custom-viewer .mims-pdf-toolbar {
  background: #1a1a1a;
  color: white;
}

Error Handling

<PDFViewer
  documentId="..."
  documentUrl="..."
  onDocumentError={(error) => {
    if (error.message.includes('404')) {
      showNotification('Document not found');
    } else if (error.message.includes('403')) {
      showNotification('Access denied');
    } else {
      showNotification('Failed to load document');
      logError(error);
    }
  }}
  onSubmitError={(error) => {
    if (error.message.includes('validation')) {
      showNotification('Please fill all required fields');
    } else {
      showNotification(`Submission failed: ${error.message}`);
    }
  }}
/>

TypeScript Types

import type {
  PDFViewerProps,
  AnyFieldDefinition,
  AnyFieldValue,
  DocumentSubmissionPayload,
  PageInfo,
  FieldType,
} from '@mims/sdk-react';

// Field types: 'text' | 'signature' | 'checkbox' | 'date' | 'initials'