Easily customize UI elements in the viewer's toolbar, buttons, and menus. Change their colors or use your own icons.
This demo allows you to:
Implementation steps
To add Toolbar Customization capability with WebViewer:
Step 1: Choose your preferred web stack
Step 2: Download required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide
Demo Dependencies
This sample uses the following:
1// ES6 Compliant Syntax
2// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-09-29
3// File: toolbar-customization/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7
8function initializeWebViewer() {
9 WebViewer(
10 {
11 path: '/lib',
12 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
13 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
14 enableMeasurement: true,
15 licenseKey: 'YOUR_LICENSE_KEY', // Replace with your license key
16 },
17 document.getElementById('viewer')
18 ).then((instance) => {
19
20 // Collect webViewer data
21 collectWebViewerData(instance);
22
23 // Customize the webviewer left panel after the load completion
24 instance.Core.documentViewer.addEventListener('documentLoaded', () => {
25 customizeUI();
26 });
27
28 console.log('WebViewer loaded successfully.');
29 }).catch((error) => {
30 console.error('Failed to initialize WebViewer:', error);
31 });
32}
33
34// UI elements section
35
36
37// Object to hold WebViewer related data
38const webViewerData = {
39 instance: null,
40 wcViewer: null,
41 windowDoc: null,
42 defaultBackgroundColor: '',
43};
44
45// Collect webViewer data
46const collectWebViewerData = (instance) => {
47 webViewerData.instance = instance;
48 webViewerData.wcViewer = document.getElementById('wc-viewer');
49 webViewerData.windowDoc = webViewerData.wcViewer?.shadowRoot;
50 let uiElement = webViewerData.windowDoc.querySelector(`[data-element='${UIElements.uiElementsMap[0].id}']`);
51 if (uiElement.style.backgroundColor !== null && uiElement.style.backgroundColor !== '')
52 webViewerData.defaultBackgroundColor = uiElement.style.backgroundColor;
53};
54
55// Customize the webviewer left panel
56const customizeUI = () => {
57 const { UI } = webViewerData.instance;
58
59 // Enable the customizable UI feature flag
60 UI.enableFeatureFlag(UI.FeatureFlags.CUSTOMIZABLE_UI);
61
62 // Enable all UI elements initially
63 UI.enableAllElements();
64
65 // Fit the page to the viewer width
66 UI.setFitMode(UI.FitMode.FitPage);
67
68 // Set the layout to single page mode
69 UI.setLayoutMode(UI.LayoutMode.Single);
70
71 // Keep the page navigation component on screen all the time
72 UI.disableFadePageNavigationComponent();
73
74 // Enable the annotation toolbar group
75 UI.enableElements(['toolbarGroup-Annotate']);
76
77 // Close the tab panel (if it's open) for refreshment.
78 UI.closeElements([UIElements.tabPanel.dataElement]);
79
80 // Get the list of registered panels in the webviewer
81 UIElements.viewerPanels = UI.getPanels();
82
83 // Find the Tab Panel to modify. The customize toolbar sub-panel will be added to this Tab panel.
84 UIElements.tabPanel.handle = UIElements.viewerPanels.find((panel) => panel.dataElement === UIElements.tabPanel.dataElement);
85
86 // Register the customize toolbar sub-panel
87 RegisterCustomizeToolbarPanel(webViewerData.instance);
88
89 // Add the new customize toolbar sub-panel to list of sub-panels under the Tab Panel
90 UIElements.customizeToolbarPanel.handle = { render: UIElements.customizeToolbarPanel.dataElement };
91 UIElements.tabPanel.handle.panelsList = [UIElements.customizeToolbarPanel.handle, ...UIElements.tabPanel.handle.panelsList];
92
93 UI.openElements([UIElements.tabPanel.dataElement]);
94};
95
96// Register the customize toolbar sub-panel
97const RegisterCustomizeToolbarPanel = () => {
98 UIElements.customizeToolbarPanel.render = UIElements.createCustomizeToolbarPanelElements();
99 webViewerData.instance.UI.addPanel({
100 dataElement: UIElements.customizeToolbarPanel.dataElement,
101 location: 'left',
102 icon: '<svg fill="#000000" width="18px" height="18px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M30 2.994h-28c-1.099 0-2 0.9-2 2v17.006c0 1.099 0.9 1.999 2 1.999h13v3.006h-5c-0.552 0-1 0.448-1 1s0.448 1 1 1h12c0.552 0 1-0.448 1-1s-0.448-1-1-1h-5v-3.006h13c1.099 0 2-0.9 2-1.999v-17.006c0-1.1-0.901-2-2-2zM30 22h-28v-17.006h28v17.006z"></path></svg>',
103 title: 'Customize Toolbar',
104 render: () => UIElements.customizeToolbarPanel.render,
105 });
106};
107
108// Enable or disable a UI element visibility
109//Made global to be accessible in ui-elements.js
110window.toggleElementVisibility = (element) => {
111 (webViewerData.instance.UI.isElementDisabled(element.id)) ?
112 webViewerData.instance.UI.enableElements([element.id]) :
113 webViewerData.instance.UI.disableElements([element.id]);
114};
115
116// Handle mouse over event, either for a checkbox or label control:
117// - change cursor to pointer
118// - toggle the UI element highlight
119window.controlOnMouseOver = (control, element) => {
120 control.style.cursor = 'pointer';
121
122 // Open the menu overlay when mouse is over the download or print button checkbox/label
123 if (element.id === 'downloadButton' || element.id === 'printButton') {
124 if (webViewerData.instance.UI.isElementDisabled('menuOverlay'))
125 webViewerData.instance.UI.enableElements('menuOverlay');
126
127 if (!webViewerData.instance.UI.isElementOpen('menuOverlay'))
128 webViewerData.instance.UI.openElements('menuOverlay');
129 }
130
131 toggleElementHighlight(element);
132};
133
134// Handle mouse leave event, either for a checkbox or label control:
135// - change cursor to default
136// - toggle the UI element highlight
137window.controlOnMouseLeave = (control, element) => {
138 control.style.cursor = 'default';
139
140 // Close the menu overlay when mouse leaves the download or print button checkbox/label
141 if (element.id === 'downloadButton' || element.id === 'printButton') {
142 if (webViewerData.instance.UI.isElementDisabled('menuOverlay'))
143 webViewerData.instance.UI.enableElements('menuOverlay');
144
145 if (webViewerData.instance.UI.isElementOpen('menuOverlay'))
146 webViewerData.instance.UI.closeElements('menuOverlay');
147 }
148
149 toggleElementHighlight(element);
150};
151
152// Highlight or reset highlight of a UI element
153window.toggleElementHighlight = (element) => {
154 let uiElement = webViewerData.windowDoc.querySelector(`[data-element='${element.id}']`);
155 if (uiElement !== null) {
156 if (uiElement.style.backgroundColor === 'orange')
157 uiElement.style.backgroundColor = webViewerData.defaultBackgroundColor;
158 else
159 uiElement.style.backgroundColor = 'orange';
160 }
161};
162
163//helper function to load the ui-elements.js script
164function loadUIElementsScript() {
165 return new Promise((resolve, reject) => {
166 if (window.UIElements) {
167 console.log('UIElements already loaded');
168 resolve();
169 return;
170 }
171
172 const script = document.createElement('script');
173 script.src = '/showcase-demos/toolbar-customization/ui-elements.js';
174 script.onload = function () {
175 console.log('✅ UIElements script loaded successfully');
176 resolve();
177 };
178 script.onerror = function () {
179 console.error('Failed to load UIElements script');
180 reject(new Error('Failed to load ui-elements.js'));
181 };
182 document.head.appendChild(script);
183 });
184}
185
186// Load UIElements script first, then initialize WebViewer
187loadUIElementsScript().then(() => {
188 initializeWebViewer();
189}).catch((error) => {
190 console.error('Failed to load UIElements:', error);
191});
192
1// ES6 Compliant Syntax
2// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-09-29
3// File: toolbar-customization/ui-elements.js
4
5// Class with static UI elements and related functions
6
7class UIElements {
8
9 // The list of registered panels in the webviewer
10 static viewerPanels = null;
11 // Convert a label to a valid ID by removing spaces
12 static labelToId = (label) => label.replace(/\s+/g, '');
13
14 // The tab panel, representing the webviewer left panel
15 static tabPanel = {
16 handle: null,
17 dataElement: 'tabPanel'
18 };
19
20 // The customize toolbar sub-panel to be registered
21 static customizeToolbarPanel = {
22 handle: null,
23 dataElement: 'customizeToolbarPanel',
24 render: null,
25 };
26
27 // The UI elements list to be used in showing / hiding controls in the webviewer
28 static uiElementsMap = [
29 {
30 label: 'Top Header',
31 id: 'default-top-header',
32 checked: true,
33 },
34 {
35 label: 'Tools Header',
36 id: 'tools-header',
37 checked: true,
38 },
39 {
40 label: 'Download PDF',
41 id: 'downloadButton',
42 checked: true,
43 },
44 {
45 label: 'Print PDF',
46 id: 'printButton',
47 checked: true,
48 },
49 {
50 label: 'Annotation Tools',
51 id: 'toolbarGroup-Annotate',
52 checked: true,
53 list: [
54 {
55 label: 'Highlight',
56 id: 'highlightToolButton',
57 checked: true,
58 },
59 {
60 label: 'Underline',
61 id: 'underlineToolButton',
62 checked: true,
63 },
64 {
65 label: 'Strikeout',
66 id: 'strikeoutToolButton',
67 checked: true,
68 },
69 {
70 label: 'Squiggly',
71 id: 'squigglyToolButton',
72 checked: true,
73 },
74 {
75 label: 'Free Hand',
76 id: 'freeHandToolButton',
77 checked: true,
78 },
79 {
80 label: 'Free Hand Highlight',
81 id: 'freeHandHighlightToolButton',
82 checked: true,
83 },
84 {
85 label: 'Free Text',
86 id: 'freeTextToolButton',
87 checked: true,
88 },
89 {
90 label: 'Insert Text',
91 id: 'markInsertTextToolButton',
92 checked: true,
93 },
94 {
95 label: 'Replace Text',
96 id: 'markReplaceTextToolButton',
97 checked: true,
98 },
99 {
100 label: 'Sticky',
101 id: 'stickyToolButton',
102 checked: true,
103 },
104 {
105 label: 'Callout',
106 id: 'calloutToolButton',
107 checked: true,
108 },
109 {
110 label: 'Eraser',
111 id: 'eraserToolButton',
112 checked: true,
113 },
114 ],
115 },
116 {
117 label: 'Overlays',
118 id: 'Overlays',
119 checked: true,
120 list: [
121 {
122 label: 'View Modes',
123 id: 'view-controls-toggle-button',
124 checked: true,
125 },
126 {
127 label: 'Left Panel',
128 id: 'leftPanelButton',
129 checked: true,
130 },
131 {
132 label: 'Search Panel',
133 id: 'searchPanelToggle',
134 checked: true,
135 },
136 {
137 label: 'Overflow Menu',
138 id: 'menuButton',
139 checked: true,
140 },
141 {
142 label: 'Page Number',
143 id: 'page-nav-floating-header',
144 checked: true,
145 },
146 ],
147 },
148 ];
149
150 // Create the customize toolbar panel elements.
151 static createCustomizeToolbarPanelElements = () => {
152 let panelDiv = document.createElement('div');
153 panelDiv.id = 'customizeToolbar';
154
155 let paragraph = document.createTextNode('A demo of the UI flexibility of WebViewer, a JavaScript-based PDF SDK for web apps. Easily hide buttons, change colors, or use your own icons via simple APIs.');
156 panelDiv.appendChild(paragraph);
157
158 let dividerDiv = document.createElement('div');
159 dividerDiv.style.borderTop = '1px solid #ccc';
160 dividerDiv.style.margin = '10px 0';
161 panelDiv.appendChild(dividerDiv);
162
163 // Hide / Show division
164 let hideShowDiv = document.createElement('div');
165 hideShowDiv.id = 'hideShow';
166
167 let hideShowTitle = document.createElement("h3");
168 hideShowTitle.textContent = "Hide / Show features in the UI";
169 hideShowDiv.appendChild(hideShowTitle);
170 hideShowDiv.appendChild(document.createElement('p'));
171 panelDiv.appendChild(hideShowDiv);
172
173 // Create checkboxes for items in the uiElementsMap array
174 this.uiElementsMap.forEach(element => {
175
176 this.createCheckbox(panelDiv, element);
177
178 let hasSubCheckboxes = (element.list && element.list.length > 0);
179 if (hasSubCheckboxes)
180 element.list.forEach(item => this.createCheckbox(panelDiv, item, hasSubCheckboxes));
181 });
182
183 panelDiv.appendChild(dividerDiv.cloneNode());
184
185 return panelDiv;
186 };
187
188 // Create a checkbox and its label, and add them to the customize toolbar panel
189 static createCheckbox = (panelDiv, element, isSubCheckbox = false) => {
190
191 // Checkbox input
192 let checkbox = document.createElement('input');
193 checkbox.type = 'checkbox';
194 checkbox.id = `${this.labelToId(element.label)}${checkbox.type}`;
195 checkbox.checked = element.checked;
196 checkbox.onmouseover = () => controlOnMouseOver(checkbox, element);
197 checkbox.onmouseleave = () => controlOnMouseLeave(checkbox, element);
198 checkbox.onclick = () => this.controlOnClick(element);
199 if (isSubCheckbox)
200 checkbox.style.marginLeft = '20px';
201
202 // Checkbox label
203 let label = document.createElement('label');
204 label.textContent = element.label;
205 label.onmouseover = () => controlOnMouseOver(label, element);
206 label.onmouseleave = () => controlOnMouseLeave(label, element);
207 label.onclick = () => {
208 checkbox.checked = !checkbox.checked;
209 this.controlOnClick(element);
210 };
211
212 panelDiv.appendChild(document.createElement('p'));
213 panelDiv.appendChild(checkbox);
214 panelDiv.appendChild(label);
215 };
216
217 // Handle click event, either for a checkbox or label control:
218 // - toggle the UI element visibility
219 // - toggle the UI element highlight
220 static controlOnClick = (element) => {
221
222 // If a UI element has sub-checkboxes, toggle them as well
223 if (element.list !== null && element.list !== undefined && element.list.length > 0) {
224 element.list.forEach(item => {
225 // Toggle the sub-checkbox UI element visibility
226 toggleElementVisibility(item);
227 const subCheckBox = this.checkBoxControl(item);
228 subCheckBox.checked = !subCheckBox.checked;
229 });
230 }
231 // Otherwise, just toggle the current UI element visibility
232 else
233 toggleElementVisibility(element);
234
235 // Toggle the UI element highlight
236 toggleElementHighlight(element);
237 };
238
239 // Get the checkbox control based on an item from the uiElementsMap array
240 static checkBoxControl = (item) => {
241 const checkbox = this.customizeToolbarPanel.render.querySelector(`#${this.labelToId(item.label)}checkbox`);
242 return checkbox;
243 };
244}
245
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales