PDF.js Express Plusplay_arrow

Professional PDF.js Viewing & Annotations - Try for free

side menu

Get Started

play_arrow

Learn more

play_arrow

Common use cases

play_arrow

Open a document

play_arrow

Save a document

play_arrow

Viewer

play_arrow

UI Customization

play_arrow

Annotations

play_arrow

Collaboration

play_arrow

Forms

play_arrow

Signature

play_arrow

Searching

play_arrow

Measurement

play_arrow

Compare

play_arrow

Advanced Capabilities

play_arrow

PDF.js Express REST API

play_arrow

Migration Guides

play_arrow

Customize toolbar in viewer

The following features are available in:

check

PDF.js Express Viewer

help_outline

PDF.js Express Viewer is a free viewer with limited capabilities compared to PDF.js Express Plus

check

PDF.js Express Plus

help_outline

PDF.js Express Plus is a commercial PDF SDK for viewing, annotating, signing, form filling and more

There are a number of ways you may want to customize the header. To name a few:

  • Removing all annotation tool buttons
  • Reordering annotation tool buttons
  • Replacing the existing view controls buttons with custom buttons
  • Adding a custom annotation tool button
  • Replace the entire header with different items in smaller screens

The PDF.js Express Web Viewer UI provides a flexible API to easily handle each of these cases and more.

To learn about structure and different types of items, you can start reading from header composition and header items. Otherwise, you can jump straight to examples and relevant APIs.

Header composition

There are two main types of headers. The first one is the top header. This header defaults to navigation tools, opening sub-menus and panels. Also in the center you will see a collection of toolbar group labels, called ribbons.

Header and json

Selecting a toolbar group other than View will reveal the tools header. Each tools header has a different combination of tools. They can be modified individually.

Header and json

Each header is represented by an array of header items. The array can be edited to add/remove/reorder items. The API, setHeaderItems, will provide the top header object as a function argument by default. To access a tools header please see the examples in the API link.

Customizing Ribbons

New in version 7.0, are the ribbons in the top header. This is a collection of toolbar groups. Each group reveals a second tools header that contain a different set of tools. There are various API calls that can be use to customize the ribbons.

To set a default to toolbar to load on startup, call setToolbarGroup. Alternatively, it can be called at any time to change the current toolbar group.

Setting the ribbon programmatically

WebViewer(...)
  .then(instance => {
    // Sets the current toolbar group
    instance.UI.setToolbarGroup('toolbarGroup-Insert');
  });

Hiding ribbons

If you want to hide a specific toolbar group, like other DOM elements, they can be hidden by using disableElements.

WebViewer(...)
  .then(instance => {
    // hide the Shapes, Edit and Insert toolbar groups.
    instance.UI.disableElements(['toolbarGroup-Shapes']);
    instance.UI.disableElements(['toolbarGroup-Edit']);
    instance.UI.disableElements(['toolbarGroup-Insert']);
  });

Hide all toolbar groups but keep one selected

Alternatively, you may only want to use one toolbar group and hide the ribbons altogether. This may be done with code similar to the following:

WebViewer(...)
  .then(instance => {
    // hide the ribbons
    instance.UI.disableElements(['ribbons']);
    // set the default toolbar group to the Shapes group
    instance.UI.setToolbarGroup('toolbarGroup-Shapes');
  });

Moving items between ribbons

Lastly, you can move specific tools from one group to another. Below is an example of moving the line tool from the Shapes toolbar group to the Annotate toolbar group.

// Moving the line tool from the 'Shapes' toolbar group to the 'Annotate' toolbar group
WebViewer(...)
  .then(function(instance) {
    instance.UI.setHeaderItems(function(header) {
      header.getHeader('toolbarGroup-Annotate').push({
        type: 'toolGroupButton',
        toolGroup: 'lineTools',
        dataElement: 'lineToolGroupButton',
        title: 'annotation.line',
      });
      header.getHeader('toolbarGroup-Shapes').delete(6);
    });
  });

Header items

Header items are objects with certain properties. If you wish to add a header item, it is important for you to understand what type it is and what properties should be used.

ActionButton

ActionButton triggers an action. The button has no active state.

Properties

  • type (string) - Must be set to actionButton.
  • img (string) - Path to an image or base64 data.
  • onClick (function) - Function to be triggered on click.
  • title (string, optional) - Tooltip of the button.
  • dataElement (string, optional) - Option to set data-element value of the button element. It can be used to disable/enable the element.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newActionButton = {
  type: 'actionButton',
  img: 'path/to/image',
  onClick: () => {
    alert('Hello world!');
  },
  dataElement: 'alertButton',
  hidden: [ 'mobile' ]
};

StatefulButton

StatefulButton is a customizable button. You can decide how many states it has, what state is active and when to update the state.

Properties

  • type (string) - Must be set to statefulButton.
  • initialState (string) - String that is one of states object's keys.
  • states (object) - Object in the shape of: { nameOfState1: state1, nameOfState2: state2, ... }
    • Properties of a state:
      • img (string, optional): Path to an image or base64 data.
      • getContent (function, optional): Function to be called when you update the state. Define this property if you don't use the img property for this button. Argument: activeState.
      • onClick (function): Function to be triggered on click. Arguments: update, activeState.
      • Any other properties you need.
  • mount (function) - Function to be called after the button is mounted into DOM
  • unmount (function, optional) - Function to be called before the button is unmounted from DOM.
  • dataElement (string, optional) - String to set data-element value of the button element. It can be used to disable/enable the element.
  • title (string, optional) - Tooltip of the button.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

A stateful button that shows the count. And when you click it, it will increment the counter by 1.

const countButton = {
  type: 'statefulButton',
  initialState: 'Count',
  states: {
    Count: {
      number: 1,
      getContent: activeState => {
        return activeState.number;
      },
      onClick: (update, activeState) => {
        activeState.number += 1;
        update();
      }
    }
  },
  dataElement: 'countButton'
};

A stateful button that shows the current page number. And when you click it, the document will go to next page. If you are already at the last page, the document will go to the first page.

const nextPageButton = {
  type: 'statefulButton',
  initialState: 'Page',
  states: {
    Page: {
      // Checkout https://www.pdftron.com/api/web/WebViewerInstance.html to see more APIs related with viewerInstance
      getContent: instance.Core.documentViewer.getCurrentPageNumber,
      onClick: () => {
        const currentPage = instance.Core.documentViewer.getCurrentPageNumber();
        const totalPages = instance.Core.documentViewer.getPageCount();
        const atLastPage = currentPage === totalPages;

        if (atLastPage) {
          instance.Core.documentViewer.setCurrentPage(1);
        } else {
          instance.Core.documentViewer.setCurrentPage(currentPage + 1);
        }
      }
    }
  },
  mount: update => {
    instance.Core.documentViewer.addEventListener('pageNumberUpdated.nextPageButton', update);
  },
  unmount: () => {
    instance.Core.documentViewer.removeEventListener('pageNumberUpdated.nextPageButton')
  },
  dataElement: 'nextPageButton'
};

A stateful button that changes the fit mode of the document.

This button is in our built-in UI, checkout it out in the demo.

Stateful Fitmode Button

const fitButton = {
  type: 'statefulButton',
  initialState: 'FitWidth',
  states: {
    FitWidth: {
      img: 'path/to/fitWidth/image',
      onClick: () => {
        instance.UI.setFitMode(instance.UI.FitMode.FitWidth);
      },
    },
    FitPage: {
      img: 'path/to/fitPage/image',
      onClick: () => {
        instance.UI.setFitMode(instance.UI.FitMode.FitPage);
      },
    }
  },
  mount: update => {
    const fitModeToState = fitMode => {
      // the returned state should be the opposite of the new current state
      // as the opposite state is what we want to switch to when the button
      // is clicked next
      if (fitMode === instance.UI.FitMode.FitPage) {
        return 'FitWidth';
      } else if (fitMode === instance.UI.FitMode.FitWidth) {
        return 'FitPage';
      }
    };

    instance.Core.documentViewer.addEventListener('fitModeUpdated.fitbutton', fitMode => {
      update(fitModeToState(fitMode));
    });
  },
  unmount: () => {
    instance.Core.documentViewer.removeEventListener('fitModeUpdated.fitbutton');
  },
  dataElement: 'fitButton',
  hidden: ['mobile']
};

ToggleElementButton

ToggleElementButton opens/closes a UI element. The button is in an active state when the UI element is open.

Properties

  • type (string) - Must be set to toggleElementButton.
  • img (string) - Path to an image or base64 data.
  • element (string) - data-element attribute value of the UI element to be opened/closed.
  • dataElement (string, optional) - Option to set data-element value of the button element. It can be used to disable/enable the element.
  • title (string, optional) - Tooltip of the button.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newToggleElementButton = {
  type: 'toggleElementButton',
  img: 'path/to/image',
  element: 'leftPanel',
  dataElement: 'leftPanelButton',
  hidden: [ 'mobile' ]
};

ToolButton

ToolButton activates a PDF.js Express Web Viewer tool. The button is in an active state when the tool is activated. To learn more about customizing annotation tools and tool buttons, see customizing tools.

Properties

  • type (string) - Must be set to toolButton.
  • toolName (string) - Name of the tool, which is either the key from toolModeMap or the name you registered your custom tool with. For custom tool registration, refer to registerTool.
  • title (string, optional) - Tooltip of the button.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newToolButton = {
  type: 'toolButton',
  toolName: 'AnnotationCreateFreeText',
  hidden: [ 'mobile' ]
}

ToolGroupButton

ToolGroupButton opens an overlay with the tools in the tool group. Unless the img option is set, the button displays the image of one of the group members. The button is in an active state when any tool in the group is active. To learn more about customizing annotation tools and tool buttons, see customizing tools.

Tool group button

Properties

  • type (string) - Must be set to 'toolGroupButton'.
  • toolGroup (string) - Name of the tool group. In the default viewer, there are freeHandTools, textTools, shapeTools and miscTools.
  • img (string, optional) - Path to an image or base64 data. Can also be the filename of a .svg from the WebViewer assets/icons/ folder (i.e. calibrate to use calibrate.svg).
  • dataElement (string, optional) - Option to set data-element value of the button element. It can be used to disable/enable the element.
  • title (string, optional) - Tooltip of the button.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newToolGroupButton = {
  type: 'toolGroupButton',
  toolGroup: 'shapeTools',
  dataElement: 'shapeToolGroupButton',
  hidden: [ 'mobile' ]
};

CustomElement

CustomElement takes a render function and renders DOM elements or React components. You may want to use this when the buttons above don't suffice.

Properties

  • type (string) - Must be set to 'customElement'.
  • title (string, optional) - Tooltip of the button.
  • render (func) - Function that returns DOM elements or React components
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const renderSlider = () => {
  const slider = document.createElement('input');
  slider.type = 'range';
  slider.oninput = () => {
    // Do something
  };

  return slider;
}

const newCustomElement = {
  type: 'customElement',
  render: renderSlider
};

In React jsx:

const Slider = () => {
  return (
    <input
      type="range"
      onInput={() => { /* Do something */ }}
    >
    </input>
  );
}

const newCustomElement = {
  type: 'customElement',
  render: () => <Slider />
};

Spacer

Spacer is just a div with flex: 1 to occupy any remaining space. It is used to push the buttons to each side on the default header.

Properties

  • type (string) - Must be set to 'spacer'.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newSpacer = {
  type: 'spacer',
  hidden: [ 'mobile' ]
};

Divider

Divider renders a vertical bar with some margin to separate item groups.

Properties

  • type (string) - Must be set to 'divider'.
  • hidden (array of strings, optional) - Option to hide the element at certain screen sizes. Accepted strings are desktop, tablet and mobile.

Example

const newDivider = {
  type: 'divider',
  hidden: [ 'mobile' ]
};

Custom Icons

All custom header buttons accept an img property. There are three different things you can pass to this property:

A path to an image

If the icon you want to use exists on your server, simply pass the path to that file like so:

const newButton = {
  img: 'path/to/image',
  ...other
};

Base 64 data

If you have a base64 string for your image, you can provide the base64 string directly:

const newButton = {
  img: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQAB...',
  ...other
};

Icon name

If you want to reuse an icon that already exists in the UI, you can simply pass the name of that icon. All existing icons can be found here.

For example, if you wanted to use the calibrate.svg icon, you would pass calibrate as the img property:

const newButton = {
  img: 'calibrate',
  ...other
};

Examples

Add a custom save button

WebViewer(...)
  .then(instance => {
    instance.UI.setHeaderItems(header => {
      header.push({
        type: 'actionButton',
        img: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>',
        onClick: () => {
          // save the annotations
        }
      });
    });
  });

After you added a custom save button, here is a code sample you can add inside of onClick function for saving a PDF.

Hiding ribbons and pushing tools to top header

WebViewer(...)
  .then(instance => {
    instance.UI.setHeaderItems(function(header) {
      // get the tools overlay
      const toolsOverlay = header.getHeader('toolbarGroup-Annotate').get('toolsOverlay');
      header.getHeader('toolbarGroup-Annotate').delete('toolsOverlay');
      // add the line tool to the top header
      header.getHeader('default').push({
        type: 'toolGroupButton',
        toolGroup: 'lineTools',
        dataElement: 'lineToolGroupButton',
        title: 'annotation.line',
      });
      // add the tools overlay to the top header
      header.push(toolsOverlay);
    });
    // hide the ribbons and second header
    instance.UI.disableElements(['ribbons']);
    instance.UI.disableElements(['toolsHeader']);
  });

Remove existing buttons

WebViewer(...)
  .then(instance => {
    instance.UI.setHeaderItems(header => {
      const items = header.getItems().slice(9, -3);
      header.update(items);
    });
  });

Reorder annotation tool buttons

WebViewer(...)
  .then(instance => {
    // remove the tools from existing group
    instance.UI.updateTool('AnnotationCreateTextHighlight', { buttonGroup: '' });
    instance.UI.updateTool('AnnotationCreateTextUnderline', { buttonGroup: '' });
    instance.UI.updateTool('AnnotationCreateTextSquiggly', { buttonGroup: '' });
    instance.UI.updateTool('AnnotationCreateTextStrikeout', { buttonGroup: '' });
    // delete default tool buttons and add new ones in desired order
    instance.UI.setHeaderItems(header => {
      const items = header.getItems();
      items.splice(10, 9,
        {
          type: 'toolButton',
          toolName: 'AnnotationCreateTextStrikeout'
        },
        {
          type: 'toolButton',
          toolName: 'AnnotationCreateTextSquiggly'
        },
        {
          type: 'toolButton',
          toolName: 'AnnotationCreateTextUnderline'
        },
        {
          type: 'toolButton',
          toolName: 'AnnotationCreateTextHighlight'
        }
      );
      header.update(items);
    });
  });
WebViewer(...)
  .then(instance => {
    instance.UI.setHeaderItems(header => {
      header.delete(9);
      header.unshift({
        type: 'customElement',
        render: () => {
          const logo = document.createElement('img');
          logo.src = 'https://www.pdftron.com/downloads/logo.svg';
          logo.style.width = '200px';
          logo.style.marginLeft = '10px';
          logo.style.cursor = 'pointer';
          logo.onclick = () => {
            window.open('https://www.pdftron.com', '_blank');
          }
          return logo;
        }
      }, {
        type: 'spacer'
      });
    });
  });

Relevant APIs

To add/remove/re-order header items, you can use the following API:

For ToolButton, make sure you register/unregister the tool using:

To switch a header group, you can use the following API: