> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-toolbar-customization.md).

# Toolbar Customization Showcase Demo Code Sample

Customize UI elements in the viewer's toolbar, buttons, or menus. Change their colors or use your own icons.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="https://showcase.apryse.com/toolbar-customization" class="button primary">Live demo</a>
{% endhint %}

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](/web/get-started/readme.md) 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-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

{% tabs %}
{% tab title="index.js" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-09-29
// File: toolbar-customization/index.js

import WebViewer from '@pdftron/webviewer';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';


function initializeWebViewer() {
  WebViewer(
    {
      path: '/lib',
      initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
      enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
      enableMeasurement: true,
      licenseKey: licenseKey, // Replace with your license key
    },
    document.getElementById('viewer')
  ).then((instance) => {

    // Collect webViewer data
    collectWebViewerData(instance);

    // Customize the webviewer left panel after the load completion
    instance.Core.documentViewer.addEventListener('documentLoaded', () => {
      customizeUI();
    });

    console.log('WebViewer loaded successfully.');
  }).catch((error) => {
    console.error('Failed to initialize WebViewer:', error);
  });
}

// UI elements section


// Object to hold WebViewer related data
const webViewerData = {
  instance: null,
  wcViewer: null,
  windowDoc: null,
  defaultBackgroundColor: '',
};

// Collect webViewer data
const collectWebViewerData = (instance) => {
  webViewerData.instance = instance;
  webViewerData.wcViewer = document.getElementById('wc-viewer');
  webViewerData.windowDoc = webViewerData.wcViewer?.shadowRoot;
  let uiElement = webViewerData.windowDoc.querySelector(`[data-element='${UIElements.uiElementsMap[0].id}']`);
  if (uiElement.style.backgroundColor !== null &#x26;&#x26; uiElement.style.backgroundColor !== '')
    webViewerData.defaultBackgroundColor = uiElement.style.backgroundColor;
};

// Customize the webviewer left panel
const customizeUI = () => {
  const { UI } = webViewerData.instance;

  // Enable the customizable UI feature flag
  UI.enableFeatureFlag(UI.FeatureFlags.CUSTOMIZABLE_UI);

  // Enable all UI elements initially
  UI.enableAllElements();

  // Fit the page to the viewer width
  UI.setFitMode(UI.FitMode.FitPage);

  // Set the layout to single page mode
  UI.setLayoutMode(UI.LayoutMode.Single);

  // Keep the page navigation component on screen all the time
  UI.disableFadePageNavigationComponent();

  // Enable the annotation toolbar group
  UI.enableElements(['toolbarGroup-Annotate']);

  // Close the tab panel (if it's open) for refreshment.
  UI.closeElements([UIElements.tabPanel.dataElement]);

  // Get the list of registered panels in the webviewer
  UIElements.viewerPanels = UI.getPanels();

  // Find the Tab Panel to modify. The customize toolbar sub-panel will be added to this Tab panel.
  UIElements.tabPanel.handle = UIElements.viewerPanels.find((panel) => panel.dataElement === UIElements.tabPanel.dataElement);

  // Register the customize toolbar sub-panel
  RegisterCustomizeToolbarPanel();

  // Add the new customize toolbar sub-panel to list of sub-panels under the Tab Panel
  UIElements.customizeToolbarPanel.handle = { render: UIElements.customizeToolbarPanel.dataElement };
  UIElements.tabPanel.handle.panelsList = [UIElements.customizeToolbarPanel.handle, ...UIElements.tabPanel.handle.panelsList];

  UI.openElements([UIElements.tabPanel.dataElement]);
};

// Register the customize toolbar sub-panel
const RegisterCustomizeToolbarPanel = () => {
  UIElements.customizeToolbarPanel.render = UIElements.createCustomizeToolbarPanelElements();
  webViewerData.instance.UI.addPanel({
    dataElement: UIElements.customizeToolbarPanel.dataElement,
    location: 'left',
    icon: '&#x3C;svg fill="#000000" width="18px" height="18px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">&#x3C;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">&#x3C;/path>&#x3C;/svg>',
    title: 'Customize Toolbar',
    render: () => UIElements.customizeToolbarPanel.render,
  });
};

// Enable or disable a UI element visibility
//Made global to be accessible in ui-elements.js
window.toggleElementVisibility = (element) => {
  (webViewerData.instance.UI.isElementDisabled(element.id)) ?
    webViewerData.instance.UI.enableElements([element.id]) :
    webViewerData.instance.UI.disableElements([element.id]);
};

// Handle mouse over event, either for a checkbox or label control:
// - change cursor to pointer
// - toggle the UI element highlight
window.controlOnMouseOver = (control, element) => {
  control.style.cursor = 'pointer';

  // Open the menu overlay when mouse is over the download or print button checkbox/label
  if (element.id === 'downloadButton' || element.id === 'printButton') {
    if (webViewerData.instance.UI.isElementDisabled('MainMenuFlyout'))
      webViewerData.instance.UI.enableElements('MainMenuFlyout');

    if (!webViewerData.instance.UI.isElementOpen('MainMenuFlyout'))
      webViewerData.instance.UI.openElements('MainMenuFlyout');
  }

  toggleElementHighlight(element);
};

// Handle mouse leave event, either for a checkbox or label control:
// - change cursor to default
// - toggle the UI element highlight
window.controlOnMouseLeave = (control, element) => {
  control.style.cursor = 'default';

  // Close the menu overlay when mouse leaves the download or print button checkbox/label
  if (element.id === 'downloadButton' || element.id === 'printButton') {
    if (webViewerData.instance.UI.isElementDisabled('MainMenuFlyout'))
      webViewerData.instance.UI.enableElements('MainMenuFlyout');

    if (webViewerData.instance.UI.isElementOpen('MainMenuFlyout'))
      webViewerData.instance.UI.closeElements('MainMenuFlyout');
  }

  toggleElementHighlight(element);
};

// Highlight or reset highlight of a UI element
window.toggleElementHighlight = (element) => {
  let uiElement = webViewerData.windowDoc.querySelector(`[data-element='${element.id}']`);
  if (uiElement !== null) {
    if (uiElement.style.backgroundColor === 'orange')
      uiElement.style.backgroundColor = webViewerData.defaultBackgroundColor;
    else
      uiElement.style.backgroundColor = 'orange';
  }
};

//helper function to load the ui-elements.js script
function loadUIElementsScript() {
  return new Promise((resolve, reject) => {
    if (window.UIElements) {
      console.log('UIElements already loaded');
      resolve();
      return;
    }

    const script = document.createElement('script');
    script.src = '/showcase-demos/toolbar-customization/ui-elements.js';
    script.onload = function () {
      console.log('✅ UIElements script loaded successfully');
      resolve();
    };
    script.onerror = function () {
      console.error('Failed to load UIElements script');
      reject(new Error('Failed to load ui-elements.js'));
    };
    document.head.appendChild(script);
  });
}

// Load UIElements script first, then initialize WebViewer
loadUIElementsScript().then(() => {
  initializeWebViewer();
}).catch((error) => {
  console.error('Failed to load UIElements:', error);
});

</code></pre>

{% endtab %}

{% tab title="ui-elements.js" %}
{% code title="ui-elements.js" lineNumbers="true" %}

```js
// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-09-29
// File: toolbar-customization/ui-elements.js

// Class with static UI elements and related functions for the toolbar customization demo

class UIElements {

    // The list of registered panels in the webviewer
    static viewerPanels = null;
    // Convert a label to a valid ID by removing spaces
    static labelToId = (label) => label.replace(/\s+/g, '');

    // The tab panel, representing the webviewer left panel
    static tabPanel = {
        handle: null,
        dataElement: 'tabPanel'
    };

    // The customize toolbar sub-panel to be registered
    static customizeToolbarPanel = {
        handle: null,
        dataElement: 'customizeToolbarPanel',
        render: null,
    };

    // The UI elements list to be used in showing / hiding controls in the webviewer
    static uiElementsMap = [
        {
            label: 'Top Header',
            id: 'default-top-header',
            checked: true,
        },
        {
            label: 'Tools Header',
            id: 'tools-header',
            checked: true,
        },
        {
            label: 'Download PDF',
            id: 'downloadButton',
            checked: true,
        },
        {
            label: 'Print PDF',
            id: 'printButton',
            checked: true,
        },
        {
            label: 'Annotation Tools',
            id: 'toolbarGroup-Annotate',
            checked: true,
            list: [
                {
                    label: 'Highlight',
                    id: 'highlightToolButton',
                    checked: true,
                },
                {
                    label: 'Underline',
                    id: 'underlineToolButton',
                    checked: true,
                },
                {
                    label: 'Strikeout',
                    id: 'strikeoutToolButton',
                    checked: true,
                },
                {
                    label: 'Squiggly',
                    id: 'squigglyToolButton',
                    checked: true,
                },
                {
                    label: 'Free Hand',
                    id: 'freeHandToolButton',
                    checked: true,
                },
                {
                    label: 'Free Hand Highlight',
                    id: 'freeHandHighlightToolButton',
                    checked: true,
                },
                {
                    label: 'Free Text',
                    id: 'freeTextToolButton',
                    checked: true,
                },
                {
                    label: 'Insert Text',
                    id: 'markInsertTextToolButton',
                    checked: true,
                },
                {
                    label: 'Replace Text',
                    id: 'markReplaceTextToolButton',
                    checked: true,
                },
                {
                    label: 'Sticky',
                    id: 'stickyToolButton',
                    checked: true,
                },
                {
                    label: 'Callout',
                    id: 'calloutToolButton',
                    checked: true,
                },
                {
                    label: 'Eraser',
                    id: 'eraserToolButton',
                    checked: true,
                },
            ],
        },
        {
            label: 'Overlays',
            id: 'Overlays',
            checked: true,
            list: [
                {
                    label: 'View Modes',
                    id: 'view-controls-toggle-button',
                    checked: true,
                },
                {
                    label: 'Left Panel',
                    id: 'leftPanelButton',
                    checked: true,
                },
                {
                    label: 'Search Panel',
                    id: 'searchPanelToggle',
                    checked: true,
                },
                {
                    label: 'Overflow Menu',
                    id: 'menuButton',
                    checked: true,
                },
                {
                    label: 'Page Number',
                    id: 'page-nav-floating-header',
                    checked: true,
                },
            ],
        },
    ];

    // Create the customize toolbar panel elements.
    static createCustomizeToolbarPanelElements = () => {
        let panelDiv = document.createElement('div');
        panelDiv.id = 'customizeToolbar';

        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.');
        panelDiv.appendChild(paragraph);

        let dividerDiv = document.createElement('div');
        dividerDiv.style.borderTop = '1px solid #ccc';
        dividerDiv.style.margin = '10px 0';
        panelDiv.appendChild(dividerDiv);

        // Hide / Show division
        let hideShowDiv = document.createElement('div');
        hideShowDiv.id = 'hideShow';

        let hideShowTitle = document.createElement("h3");
        hideShowTitle.textContent = "Hide / Show features in the UI";
        hideShowDiv.appendChild(hideShowTitle);
        hideShowDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(hideShowDiv);

        // Create checkboxes for items in the uiElementsMap array
        this.uiElementsMap.forEach(element => {

            this.createCheckbox(panelDiv, element);

            let hasSubCheckboxes = (element.list && element.list.length > 0);
            if (hasSubCheckboxes)
                element.list.forEach(item => this.createCheckbox(panelDiv, item, hasSubCheckboxes));
        });

        panelDiv.appendChild(dividerDiv.cloneNode());
 
        return panelDiv;
    };

    // Create a checkbox and its label, and add them to the customize toolbar panel
    static createCheckbox = (panelDiv, element, isSubCheckbox = false) => {

        // Checkbox input
        let checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.id = `${this.labelToId(element.label)}${checkbox.type}`;
        checkbox.checked = element.checked;
        checkbox.onmouseover = () => controlOnMouseOver(checkbox, element);
        checkbox.onmouseleave = () => controlOnMouseLeave(checkbox, element);
        checkbox.onclick = () => this.controlOnClick(element);
        if (isSubCheckbox)
            checkbox.style.marginLeft = '20px';

        // Checkbox label
        let label = document.createElement('label');
        label.textContent = element.label;
        label.onmouseover = () => controlOnMouseOver(label, element);
        label.onmouseleave = () => controlOnMouseLeave(label, element);
        label.onclick = () => {
            checkbox.checked = !checkbox.checked;
            this.controlOnClick(element);
        };

        panelDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(checkbox);
        panelDiv.appendChild(label);
    };

    // Handle click event, either for a checkbox or label control:
    // - toggle the UI element visibility
    // - toggle the UI element highlight
    static controlOnClick = (element) => {

        // If a UI element has sub-checkboxes, toggle them as well
        if (element.list !== null && element.list !== undefined && element.list.length > 0) {
            element.list.forEach(item => {
                // Toggle the sub-checkbox UI element visibility
                toggleElementVisibility(item);
                const subCheckBox = this.checkBoxControl(item);
                subCheckBox.checked = !subCheckBox.checked;
            });
        }
        // Otherwise, just toggle the current UI element visibility
        else
            toggleElementVisibility(element);

        // Toggle the UI element highlight
        toggleElementHighlight(element);
    };
    
    // Get the checkbox control based on an item from the uiElementsMap array
    static checkBoxControl = (item) => {
        const checkbox = this.customizeToolbarPanel.render.querySelector(`#${this.labelToId(item.label)}checkbox`);
        return checkbox;
    };
}

```

{% endcode %}
{% endtab %}
{% endtabs %}

[View the full sample on GitHub](https://github.com/ApryseSDK/webviewer-samples/tree/main/showcase-demos-playground)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-toolbar-customization.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
