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
8// Function to initialize and load the WebViewer with user bookmarks feature.
9function addUserBookmarks() {
10 const element = document.getElementById('viewer');
11 if (!element) {
12 console.error('Viewer div not found.');
13 return;
14 }
15
16 WebViewer({
17 path: '/lib',
18 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/Report_2011.pdf',
19 licenseKey: 'YOUR_LICENSE_KEY',
20 enableFilePicker: true,
21 }, element).then(instance => {
22
23 instance.UI.enableFeatures([instance.UI.Feature.Initials]);
24
25 // Reset state on document change.
26 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
27 instance.UI.openElements(['tabPanel']);
28 instance.UI.setActiveTabInPanel({ tabPanel: 'tabPanel', tabName: 'bookmarksPanel' });
29 instance.UI.enableBookmarkIconShortcutVisibility();
30
31 setBookmarks({});
32 setBookmarkName('My Bookmark');
33 setPageNumber(1);
34 createBookmarkControls();
35 });
36
37 instance.UI.addEventListener('userBookmarksChanged', (e) => {
38 console.log('User bookmarks updated:', e.detail);
39
40 });
41 });
42}
43// Bookmarks state
44let bookmarks = {};
45
46//Usage: 'setBookmarks({ ...bookmarks, [pageNumber]: bookmarkName });'
47function setBookmarks(newBookmarks) {
48 bookmarks = newBookmarks;
49 // Optionally, update the UI or perform other actions here.
50}
51
52// Page number state
53let pageNumber = 1;
54function setPageNumber(val) {
55 pageNumber = val;
56}
57
58// Bookmark name state
59let bookmarkName = 'My Bookmark';
60function setBookmarkName(val) {
61 bookmarkName = val;
62}
63
64// Error state
65let error = '';
66function setError(val) {
67 error = val;
68 // if empty, or null, hide error div.
69 if (val && val !== '' && val.length !== 0) {
70 console.error('Bookmark Error:', val);
71
72 } else {
73 console.log('Clearing bookmark error');
74 }
75
76 const errorDiv = document.querySelector('#bookmark-error-div');
77 if (errorDiv) {
78 errorDiv.textContent = val;
79 errorDiv.style.display = 'block';
80 }
81}
82
83// Button enabled state
84let isButtonEnabled = true;
85function setIsButtonEnabled(enabled) {
86 isButtonEnabled = enabled;
87 const button = document.querySelector('.bookmark-add-btn');
88 if (button) {
89 button.disabled = !enabled;
90 }
91}
92
93// Function to create a new bookmark.
94function createNewBookmark() {
95 const doc = WebViewer.getInstance().Core.documentViewer.getDocument();
96 const bookmarksObject = bookmarks;
97 const bookmarkName = document.querySelector('.bookmark-title-input').value || 'My Bookmark';
98 setPageNumber(document.querySelector('.bookmark-page-input').value || 1);
99
100 console.log('Creating new bookmark at page', pageNumber, 'with name', bookmarkName);
101
102 // Validate bookmarksObject exists.
103 if (!bookmarksObject) {
104 console.log('Bookmarks feature is not available.');
105 return;
106 }
107
108 // Validate doc exists.
109 if (!doc) {
110 console.log('Document is not loaded.');
111 return;
112 }
113
114 // Validate pageNumber is valid.
115 if (isNaN(pageNumber) || pageNumber < 1 || pageNumber > doc.getPageCount()) {
116 setError('Please enter a valid page number.');
117 } else {
118 bookmarks[pageNumber - 1] = bookmarkName;
119 WebViewer.getInstance().UI.importBookmarks(bookmarksObject);
120 setBookmarks({ ...bookmarksObject, [pageNumber - 1]: bookmarkName });
121 setIsButtonEnabled(true);
122 setError('');
123 }
124}
125
126// Function to created bookmark controls.
127function createBookmarkControls() {
128 console.log('Loading bookmark controls');
129
130//Checking if controls already exist.
131
132 const existingControls = document.getElementById('controls-container');
133 if (existingControls) {
134 resetBookmarkControls();
135 console.log('Bookmark controls already exist');
136 return;
137 }
138
139 // Create a container for all controls (label, dropdown, and buttons).
140 const controlsContainer = document.createElement('div');
141 controlsContainer.className = 'control-container';
142 controlsContainer.id = 'controls-container';
143 const element = document.getElementById('viewer');
144 if (!element) {
145 console.error('Viewer div not found.');
146 return;
147 }
148 element.insertBefore(controlsContainer, element.firstChild);
149
150 console.log('controls.js loaded, createBookmarkControls:', window.createBookmarkControls);
151 // Dynamically load controls.js if not already loaded.
152 if (!window.createBookmarkControls) {
153 const script = document.createElement('script');
154 script.src = '/showcase-demos/add-personal-bookmark-pdf/controls.js';
155 script.onload = function () {
156 console.log('Loaded controls', createControls);
157 console.log('createNewBookmarks:', createNewBookmark);
158 console.log('resetBookmarkControls:', resetBookmarkControls);
159
160 createControls('controls-container', createNewBookmark, resetBookmarkControls);
161
162 };
163 document.head.appendChild(script);
164 }
165}
166
167function resetBookmarkControls() {
168
169 setBookmarks({});//Clear bookmarks state.
170 setPageNumber(1);
171 setBookmarkName('My Bookmark');
172 setError('');
173 setIsButtonEnabled(true);
174
175 console.log('Bookmark controls reset');
176}
177
178// Initialize the WebViewer and add user bookmarks feature.
179addUserBookmarks();
180
181
182
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