Add bookmarks to quickly return to specific pages within your PDF document.
This demo allows you to:
Implementation steps
To add user bookmark capability with WebViewer:
Step 1: Get started with your preferred web stack for WebViewer
Step 2: Add the ES6 JavaScript sample code provided in this guide
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), 2025-09-17
3// File: showcase-demos/add-personal-bookmark-pdf/index.js
4
5
6import WebViewer from '@pdftron/webviewer';
7
8const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
9
10// Function to initialize and load the WebViewer with user bookmarks feature
11function addUserBookmarks() {
12 const element = document.getElementById('viewer');
13 if (!element) {
14 console.error('Viewer div not found.');
15 return;
16 }
17
18 WebViewer({
19 path: '/lib',
20 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/Report_2011.pdf',
21 licenseKey: licenseKey,
22 enableFilePicker: true,
23 }, element).then(instance => {
24
25 instance.UI.enableFeatures([instance.UI.Feature.Initials]);
26
27 // Reset state on document change
28 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
29 instance.UI.openElements(['tabPanel']);
30 instance.UI.setActiveTabInPanel({ tabPanel: 'tabPanel', tabName: 'bookmarksPanel' });
31 instance.UI.enableBookmarkIconShortcutVisibility();
32
33 setBookmarks({});
34 setBookmarkName('My Bookmark');
35 setPageNumber(1);
36 createBookmarkControls();
37 });
38
39 instance.UI.addEventListener('userBookmarksChanged', (e) => {
40 console.log('User bookmarks updated:', e.detail);
41
42 });
43 });
44}
45// Bookmarks state
46let bookmarks = {};
47
48//Usage: 'setBookmarks({ ...bookmarks, [pageNumber]: bookmarkName });'
49function setBookmarks(newBookmarks) {
50 bookmarks = newBookmarks;
51 // Optionally, update the UI or perform other actions here
52}
53
54// Page number state
55let pageNumber = 1;
56function setPageNumber(val) {
57 pageNumber = val;
58}
59
60// Bookmark name state
61let bookmarkName = 'My Bookmark';
62function setBookmarkName(val) {
63 bookmarkName = val;
64}
65
66// Error state
67let error = '';
68function setError(val) {
69 error = val;
70 // if empty, or null, hide error div
71 if (val && val !== '' && val.length !== 0) {
72 console.error('Bookmark Error:', val);
73
74 } else {
75 console.log('Clearing bookmark error');
76 }
77
78 const errorDiv = document.querySelector('#bookmark-error-div');
79 if (errorDiv) {
80 errorDiv.textContent = val;
81 errorDiv.style.display = 'block';
82 }
83}
84
85// Button enabled state
86let isButtonEnabled = true;
87function setIsButtonEnabled(enabled) {
88 isButtonEnabled = enabled;
89 const button = document.querySelector('.bookmark-add-btn');
90 if (button) {
91 button.disabled = !enabled;
92 }
93}
94
95// Function to create a new bookmark
96function createNewBookmark() {
97 const doc = WebViewer.getInstance().Core.documentViewer.getDocument();
98 const bookmarksObject = bookmarks;
99 const bookmarkName = document.querySelector('.bookmark-title-input').value || 'My Bookmark';
100 setPageNumber(document.querySelector('.bookmark-page-input').value || 1);
101
102 console.log('Creating new bookmark at page', pageNumber, 'with name', bookmarkName);
103
104 // validate bookmarksObject exists
105 if (!bookmarksObject) {
106 console.log('Bookmarks feature is not available.');
107 return;
108 }
109
110 // validate doc exists
111 if (!doc) {
112 console.log('Document is not loaded.');
113 return;
114 }
115
116 // validate pageNumber is valid
117 if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > doc.getPageCount()) {
118 setError('Please enter a valid page number.');
119 } else {
120 bookmarks[pageNumber - 1] = bookmarkName;
121 WebViewer.getInstance().UI.importBookmarks(bookmarksObject);
122 setBookmarks({ ...bookmarksObject, [pageNumber - 1]: bookmarkName });
123 setIsButtonEnabled(true);
124 setError('');
125 }
126}
127
128// Function to created bookmark controls
129function createBookmarkControls() {
130 console.log('Loading bookmark controls');
131
132//Checking if controls already exist
133
134 const existingControls = document.getElementById('controls-container');
135 if (existingControls) {
136 resetBookmarkControls();
137 console.log('Bookmark controls already exist');
138 return;
139 }
140
141 // Create a container for all controls (label, dropdown, and buttons)
142 const controlsContainer = document.createElement('div');
143 controlsContainer.className = 'control-container';
144 controlsContainer.id = 'controls-container';
145 const element = document.getElementById('viewer');
146 if (!element) {
147 console.error('Viewer div not found.');
148 return;
149 }
150 element.insertBefore(controlsContainer, element.firstChild);
151
152 console.log('controls.js loaded, createBookmarkControls:', window.createBookmarkControls);
153 // Dynamically load controls.js if not already loaded
154 if (!window.createBookmarkControls) {
155 const script = document.createElement('script');
156 script.src = '/showcase-demos/add-personal-bookmark-pdf/controls.js';
157 script.onload = function () {
158 console.log('Loaded controls', createControls);
159 console.log('createNewBookmarks:', createNewBookmark);
160 console.log('resetBookmarkControls:', resetBookmarkControls);
161
162 createControls('controls-container', createNewBookmark, resetBookmarkControls);
163
164 };
165 document.head.appendChild(script);
166 }
167}
168
169function resetBookmarkControls() {
170
171 setBookmarks({});//Clear bookmarks state
172 setPageNumber(1);
173 setBookmarkName('My Bookmark');
174 setError('');
175 setIsButtonEnabled(true);
176
177 console.log('Bookmark controls reset');
178}
179
180// Initialize the WebViewer and add user bookmarks feature
181addUserBookmarks();
182
183
184
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), 2025-09-17
3// File: showcase-demos/add-personal-bookmark-pdf/controls.js
4
5function createControls(targetDivId, createNewBookmark, resetBookmarkControls) {
6 const container = document.getElementById(targetDivId);
7 if (!container) {
8 console.error('Target div not found:', targetDivId);
9 return;
10 }
11 container.classList.add('bookmark-controls-container');
12
13 // 1. Bolded element with text "Bookmark Details"
14 const detailsP = document.createElement('p');
15 detailsP.textContent = 'Bookmark Details';
16 detailsP.style.fontWeight = 'bold';
17 container.appendChild(detailsP);
18
19 // 2. <p> with text "Bookmark Title:"
20 const titleLabel = document.createElement('p');
21 titleLabel.textContent = 'Bookmark Title:';
22 container.appendChild(titleLabel);
23
24 // 3. Textbox for bookmark title
25 const titleInput = document.createElement('input');
26 titleInput.type = 'text';
27 titleInput.value = 'My Bookmark';
28 titleInput.className = 'bookmark-title-input';
29 container.appendChild(titleInput);
30
31 // 4. <p> with text "Destination Page:"
32 const pageLabel = document.createElement('p');
33 pageLabel.textContent = 'Destination Page:';
34 container.appendChild(pageLabel);
35
36 // 5. Textbox for destination page
37 const pageInput = document.createElement('input');
38 pageInput.type = 'text';
39 pageInput.value = '1';
40 pageInput.className = 'bookmark-page-input';
41 container.appendChild(pageInput);
42
43 // 6. Button to add new bookmark
44 const addButton = document.createElement('button');
45 addButton.textContent = 'Add New Booknark';
46 addButton.className = 'bookmark-add-btn';
47 addButton.onclick = function () {
48 createNewBookmark();
49 };
50 container.appendChild(addButton);
51
52 //Add error div
53 const errorDiv = document.createElement('div');
54 errorDiv.style.color = 'red';
55 errorDiv.style.display = 'none';
56 errorDiv.id = 'bookmark-error-div';
57 container.appendChild(errorDiv);
58}
59
1/* Layout for viewer and its children */
2#viewer {
3 display: flex;
4 margin: 0 0 0 0;
5 width: 100%;
6}
7
8#playground-viewer{
9 height: 90% !important;
10}
11
12/* Controls Container on the left */
13.control-container {
14 width: 150px;
15 overflow-x: hidden;
16 margin: auto;
17 padding-bottom: 50px;
18 box-sizing: border-box;
19 display: flex;
20 flex-direction: column;
21 align-items: flex-start;
22 gap: 1px;
23 margin: 0;
24 padding: 10px 5px 5px 10px;
25 border-right: 1px solid #e0e0e0;
26 background-color: rgba(112, 198, 255, 0.2);
27 /* Light blue background for contrast */
28}
29
30.bottom-headers-wrapper {
31 bottom: -11px !important;
32}
33
34#editorWrapper {
35 width: 100% !important;
36 height: 85% !important;
37 padding-bottom: 50px !important;
38 margin-bottom: 250px !important;
39}
40
41/* WebViewer container on the right */
42#webviewer-1 {
43 margin: 0;
44 padding-bottom: 10px !important;
45
46
47}
48
49/* Responsive Design */
50@media (max-width: 768px) {
51 #viewer {
52 flex-direction: column;
53 }
54
55 .control-container {
56 width: 150px;
57 height: auto;
58 border-right: none;
59 border-bottom: 1px solid #e0e0e0;
60 align-items: center;
61 }
62
63
64}
65
66
67#GenericFileTab{
68 margin-bottom: 50px !important;
69}
70
71/* Gallery Picker Styles */
72.gallery-grid {
73 display: grid;
74 grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
75 gap: 16px;
76 overflow-y: auto;
77 max-height: 100vh;
78 padding: 10px;
79}
80
81.gallery-thumb {
82 cursor: pointer;
83 border: 1px solid #ccc;
84 border-radius: 6px;
85 background: #fff;
86 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
87 display: flex;
88 flex-direction: column;
89 align-items: center;
90 padding: 8px;
91 transition: box-shadow 0.2s;
92}
93
94.gallery-thumb:hover,
95.gallery-thumb:focus {
96 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
97 border-color: #70c6ff;
98}
99
100.gallery-thumb-img {
101 width: 100px;
102 height: 100px;
103 object-fit: cover;
104 border-radius: 4px;
105 margin-bottom: 6px;
106}
107
108.gallery-thumb-label {
109 font-size: 13px;
110 color: #333;
111 text-align: center;
112 margin-top: 2px;
113 word-break: break-word;
114}
1.bookmark-controls-container {
2 display: flex;
3 flex-direction: column;
4 gap: 10px;
5 max-width: 300px;
6 margin: 0 auto;
7}
8
9.bookmark-title-input,
10.bookmark-page-input {
11 width: 100%;
12 padding: 6px 8px;
13 font-size: 1em;
14 border: 1px solid #ccc;
15 border-radius: 4px;
16}
17
18.bookmark-add-btn {
19 padding: 8px 12px;
20 font-size: 1em;
21 background: #1976d2;
22 color: #fff;
23 border: none;
24 border-radius: 4px;
25 cursor: pointer;
26 font-weight: bold;
27 transition: background 0.2s;
28}
29
30.bookmark-add-btn:hover {
31 background: #1565c0;
32}
33
34.bookmark-controls-container p {
35 margin: 0 0 2px 0;
36}
37
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales