PDFViewer

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

Import

import { PDFViewer } from '@doccentral/react';

Basic Usage

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

Props

Required Props

PropTypeDefaultDescription
documentId*string-Unique identifier for the document. Used for submission tracking.
documentUrl*string-URL 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.
classNamestring-Additional CSS class names for the container.
styleReact.CSSProperties-Inline styles for the container.

Initial Data

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

Event Callbacks

PropTypeDefaultDescription
onDocumentLoad(pages: PageInfo[]) => void-Called when the PDF is successfully loaded.
onDocumentError(error: Error) => void-Called when the PDF fails to load.
onFieldAdd(field: AnyFieldDefinition) => void-Called when a new field is added.
onFieldUpdate(fieldId: string, updates: Partial<AnyFieldDefinition>) => void-Called when a field position or properties change.
onFieldValueChange(fieldId: string, value: AnyFieldValue) => void-Called when a field value changes.
onFieldRemove(fieldId: string) => void-Called 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) => void-Called 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 DocCentral 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 .doccentral-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 '@doccentral/react';

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