Toolbar Customization Showcase Demo Code Sample

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:

  • Hover over the listed UI elements to highlight them
  • Visually identify the element
  • Show or hide specific elements using checkboxes

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:

Want to see a live version of this demo?

Try the Toolbar Customization demo

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

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales