Custom UI Enhancements to WebViewer with React

This sample demonstrates several features to customize WebViewer into a React app. The UI customization includes:

  • How to leverage Apryse's document renderer without an <iFrame>
  • How to define custom <button> elements and implement functionality from the Apryse SDK such as
    • Zoom In/Out
    • Drawing Rectangles
    • Select Tool
    • Creating and Applying Redactions
  • How to implement searching using DocViewer Search APIs

WebViewer provides a slick out-of-the-box responsive UI that enables you to view, annotate and manipulate PDFs and other document types inside any web project. Check out getting started with WebViewer for React.

Click the button below to view the full project in GitHub.

1import React, { useRef, useEffect, useState } from 'react';
2import SearchContainer from './components/SearchContainer';
3import { ReactComponent as ZoomIn } from './assets/icons/ic_zoom_in_black_24px.svg';
4import { ReactComponent as ZoomOut } from './assets/icons/ic_zoom_out_black_24px.svg';
5import { ReactComponent as AnnotationRectangle } from './assets/icons/ic_annotation_square_black_24px.svg';
6import { ReactComponent as AnnotationRedact } from './assets/icons/ic_annotation_add_redact_black_24px.svg';
7import { ReactComponent as AnnotationApplyRedact} from './assets/icons/ic_annotation_apply_redact_black_24px.svg';
8import { ReactComponent as Search } from './assets/icons/ic_search_black_24px.svg';
9import { ReactComponent as Select } from './assets/icons/ic_select_black_24px.svg';
10import { ReactComponent as EditContent } from './assets/icons/ic_edit_page_24px.svg';
11import { ReactComponent as AddParagraph } from './assets/icons/ic_paragraph_24px.svg';
12import { ReactComponent as AddImageContent } from './assets/icons/ic_add_image_24px.svg';
13import './App.css';
14
15/* global globalThis */
16const root = globalThis;
17
18const App = () => {
19 const viewer = useRef(null);
20 const scrollView = useRef(null);
21 const searchTerm = useRef(null);
22 const searchContainerRef = useRef(null);
23
24 const [documentViewer, setDocumentViewer] = useState(null);
25 const [annotationManager, setAnnotationManager] = useState(null);
26 const [searchContainerOpen, setSearchContainerOpen] = useState(false);
27 const [isInContentEditMode, setIsInContentEditMode] = useState(false);
28
29 const Annotations = root.Core?.Annotations;
30
31 // if using a class, equivalent of componentDidMount
32 useEffect(() => {
33 let pollTimer;
34
35 const initViewer = () => {
36 const Core = root.Core;
37 if (!Core) {
38 pollTimer = setTimeout(initViewer, 50);
39 return;
40 }
41
42 Core.setWorkerPath('/webviewer');
43 Core.enableFullPDF();
44
45 const documentViewer = new Core.DocumentViewer();
46 documentViewer.setScrollViewElement(scrollView.current);
47 documentViewer.setViewerElement(viewer.current);
48 documentViewer.enableAnnotations();
49 documentViewer.loadDocument('/files/demo.pdf');
50
51 setDocumentViewer(documentViewer);
52
53 documentViewer.addEventListener('documentLoaded', () => {
54 console.log('document loaded');
55 documentViewer.setToolMode(documentViewer.getTool(Core.Tools.ToolNames.EDIT));
56 setAnnotationManager(documentViewer.getAnnotationManager());
57 });
58 };
59
60 initViewer();
61
62 return () => {
63 if (pollTimer) {
64 clearTimeout(pollTimer);
65 }
66 };
67 }, []);
68
69 const zoomOut = () => {
70 documentViewer.zoomTo(documentViewer.getZoomLevel() - 0.25);
71 };
72
73 const zoomIn = () => {
74 documentViewer.zoomTo(documentViewer.getZoomLevel() + 0.25);
75 };
76
77 const startEditingContent = () => {
78 const contentEditManager = documentViewer.getContentEditManager();
79 contentEditManager.startContentEditMode();
80 setIsInContentEditMode(true);
81 }
82
83 const endEditingContent = () => {
84 setIsInContentEditMode(false);
85 documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.EDIT));
86 const contentEditManager = documentViewer.getContentEditManager();
87 contentEditManager.endContentEditMode();
88 }
89
90 const addParagraph = () => {
91 if (isInContentEditMode) {
92 const addParagraphTool = documentViewer.getTool(root.Core.Tools.ToolNames.ADD_PARAGRAPH);
93 documentViewer.setToolMode(addParagraphTool);
94 } else {
95 alert('Content Edit mode is not enabled.')
96 }
97 };
98
99 const addImageContent = () => {
100 if (isInContentEditMode) {
101 const addImageContentTool = documentViewer.getTool(root.Core.Tools.ToolNames.ADD_IMAGE_CONTENT);
102 documentViewer.setToolMode(addImageContentTool);
103 } else {
104 alert('Content Edit mode is not enabled.')
105 }
106 };
107
108 const createRectangle = () => {
109 documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.RECTANGLE));
110 };
111
112 const selectTool = () => {
113 documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.EDIT));
114 };
115
116 const createRedaction = () => {
117 documentViewer.setToolMode(documentViewer.getTool(root.Core.Tools.ToolNames.REDACTION));
118 };
119
120 const applyRedactions = async () => {
121 const annotationManager = documentViewer.getAnnotationManager();
122 annotationManager.enableRedaction();
123 await annotationManager.applyRedactions();
124 };
125
126 return (
127 <div className="App">
128 <div id="main-column">
129 <div className="center" id="tools">
130 <button onClick={zoomOut}>
131 <ZoomOut />
132 </button>
133 <button onClick={zoomIn}>
134 <ZoomIn />
135 </button>
136 <button onClick={startEditingContent} title="Switch to edit mode">
137 <EditContent />
138 </button>
139 <button onClick={addParagraph} title="Add new paragraph">
140 <AddParagraph />
141 </button>
142 <button onClick={addImageContent} title="Add new content image">
143 <AddImageContent />
144 </button>
145 <button onClick={endEditingContent} title="End edit mode">
146 Finish Editing
147 </button>
148 <button onClick={createRectangle}>
149 <AnnotationRectangle />
150 </button>
151 <button onClick={createRedaction} title="Create Redaction">
152 <AnnotationRedact />
153 </button>
154 <button onClick={applyRedactions} title="Apply Redactions">
155 <AnnotationApplyRedact />
156 </button>
157 <button onClick={selectTool}>
158 <Select />
159 </button>
160 <button
161 onClick={() => {
162 // Flip the boolean
163 setSearchContainerOpen(prevState => !prevState);
164 }}
165 >
166 <Search />
167 </button>
168 </div>
169 <div className="flexbox-container" id="scroll-view" ref={scrollView}>
170 <div id="viewer" ref={viewer}></div>
171 </div>
172 </div>
173 <div className="flexbox-container">
174 <SearchContainer
175 Annotations={Annotations}
176 annotationManager={annotationManager}
177 documentViewer={documentViewer}
178 searchTermRef={searchTerm}
179 searchContainerRef={searchContainerRef}
180 open={searchContainerOpen}
181 />
182 </div>
183 </div>
184 );
185};
186
187export default App;
188

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales