Multi-Tab Support Showcase Demo Code Sample

Requirements
View Demo

Easily launch and manage multiple documents concurrently with optimized performance across multiple tabs.

This demo allows you to:

  • Load multiple PDFs in their own tab
  • Edit each PDF file independently
  • Download the updated PDFs

Implementation steps
To add multi-tab capability to WebViewer:

Step 1: Get started with WebViewer in your preferred web stack
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.

License Key

1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 3.5, 2025-08-11
3// File: multi-tab-support/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
8
9const element = document.getElementById('viewer');
10let theInstance = null;
11
12const onLoad = async (instance) => {
13 theInstance = instance;
14 // Enable multi-tab support
15 instance.UI.enableFeatures([instance.UI.Feature.MultiTab]);
16 // Add default tabs
17 instance.UI.TabManager.addTab(firstDefaultDocument.path, firstDefaultDocument.options);
18 instance.UI.TabManager.addTab(secondDefaultDocument.path, secondDefaultDocument.options);
19 instance.UI.TabManager.addTab(thirdDefaultDocument.path, thirdDefaultDocument.options);
20 const allTabs = instance.UI.TabManager.getAllTabs();
21 instance.UI.TabManager.setActiveTab(allTabs?.[0]?.id || 1);
22
23 // Set up event listeners for tab management
24 instance.UI.addEventListener(instance.UI.Events.TAB_ADDED, updateTabs);
25 instance.UI.addEventListener(instance.UI.Events.TAB_DELETED, tabDeleted);
26 instance.UI.addEventListener(instance.UI.Events.TAB_MOVED, tabMoved);
27 instance.UI.addEventListener(instance.UI.Events.BEFORE_TAB_CHANGED, changeActiveTab);
28 // Update the tabs UI (buttons to select and delete tabs)
29 updateTabs();
30 // Add the Add Tab button at the end of the controls container
31 controlsContainer.appendChild(buttonNewTab);
32};
33
34WebViewer(
35 {
36 path: '/lib',
37 licenseKey: licenseKey,
38 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
39 },
40 element
41).then((instance) => {
42 onLoad(instance);
43});
44
45// UI elements corresponding to document tabs
46// Each tab will have a button to select (activate) it and a button to delete it
47let tabButtons = [];
48
49// preserve the active tab ID to restore the active tab after deletion or movement
50let activeTabID = -1; // -1 means no active tab
51
52// listener for moving tab event
53function tabMoved() {
54 activeTabID = theInstance.UI.TabManager.getActiveTab().id;
55 updateTabs();
56}
57
58// listener for tab deletion
59function tabDeleted(){
60 const tabManager = theInstance.UI.TabManager;
61 try {
62 activeTabID = tabManager.getActiveTab().id; // preserve the active tab ID if it exists
63 } catch (error) {
64 activeTabID = -1; // if the user deletes the active tab, we reset the active tab ID
65 }
66 updateTabs();
67 const allTabs = tabManager.getAllTabs()
68 if(allTabs.length > 1) { //force refreshing after deletion
69 const saveActiveTabID = tabManager.getActiveTab().id;
70 allTabs.forEach(tab => {
71 if(tab.id != saveActiveTabID) {
72 tabManager.setActiveTab(tab.id);
73 }
74 });
75 tabManager.setActiveTab(saveActiveTabID);
76 }
77}
78
79// common function to update the tab buttons in the UI
80// This function will be called for any change affecting tabs, which includes:
81// 1. After default tabs are created
82// 2. When the active tab changes
83// 3. When a tab is added, deleted, or moved
84function updateTabs(){
85 // Remove all tab elements from container and reset the array
86 // Rebuilding the buttons array is an easy way to keep the buttons in sync with the tabs
87 tabButtons.forEach(element => element.remove());
88 tabButtons = [];
89 const tabManager = theInstance.UI.TabManager;
90 const allTabs = tabManager.getAllTabs();
91
92 allTabs.forEach(tab => {
93 // Create a button to select the tab (make it active)
94 const buttonSelectTab = document.createElement('button');
95 tabButtons.push(buttonSelectTab);
96 buttonSelectTab.textContent = tab.options.filename;
97 buttonSelectTab.className = 'btn-select';
98 // The container inlucdes a line break to separate the tab buttons from the AddTab button.
99 // Insert the button before the line break
100 controlsContainer.insertBefore(buttonSelectTab, lineBreak);
101 // Add an event handler to select the tab when clicked
102 buttonSelectTab.onclick = async () => {
103 theInstance.UI.TabManager.setActiveTab(tab.id);
104 };
105 // Create a button to delete the tab
106 const buttonDelTab = document.createElement('button');
107 tabButtons.push(buttonDelTab);
108 buttonDelTab.textContent = 'x';
109 buttonDelTab.className = 'btn-del';
110 controlsContainer.insertBefore(buttonDelTab, lineBreak);
111 // Add an event handler to delete the tab when clicked
112 buttonDelTab.onclick = async () => {
113 activeTabID = theInstance.UI.TabManager.getActiveTab().id;
114 theInstance.UI.TabManager.deleteTab(tab.id);
115 };
116 });
117
118 if(allTabs.length < 1)
119 return; // No tabs to display
120
121 let activeTabIndex = 0;
122 if(activeTabID < 0) // if no active tab ID is preserved, try to find the active tab
123 {
124 try{
125 activeTabIndex = allTabs.indexOf(tabManager.getActiveTab());
126 } catch (error){
127 // activeTabIndex will remain 0 if no active tab is found
128 }
129 } else {
130 // If we have an active tab ID, find its index and set the button class accordingly
131 activeTabIndex = allTabs.findIndex(obj => obj.id === activeTabID);
132 }
133 // Set the button styles based on the active tab index
134 setButtonStyles(activeTabIndex);
135}
136
137// Function to set styles for the tab buttons based on the active tab index
138// This function will be called whenever the active tab changes or tabs are added/deleted/moved
139// It updates the class of the buttons to indicate which tab is active
140function setButtonStyles(activeTabIndex) {
141 tabButtons.forEach((button, index) => {
142 if(index % 2 === 0){ // Only update the select buttons (even indices)
143 button.className = (activeTabIndex * 2 === index) ? 'btn-active' : 'btn-select';
144 }
145 });
146}
147
148// Listener for tab change events
149function changeActiveTab(prevTab, nextTab) {
150 if (activeTabID === nextTab.id)
151 return;
152
153 activeTabID = nextTab.id;
154 const allTabs = theInstance.UI.TabManager.getAllTabs();
155 const activeTabIndex = allTabs.findIndex(tab => tab.id === activeTabID);
156 setButtonStyles(activeTabIndex);
157}
158
159// default documents to load
160const firstDefaultDocument = {
161 path: 'https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf',
162 options: {
163 extension: 'pdf',
164 filename: 'Demo PDF',
165 setActive: true,
166 saveCurrentActiveTabState: true,
167 },
168};
169
170const secondDefaultDocument = {
171 path: 'https://apryse.s3.amazonaws.com/public/files/samples/sales_tracker.xlsx',
172 options: {
173 extension: 'xlsx',
174 filename: 'Sales Tracker (xlsx)',
175 setActive: false,
176 saveCurrentActiveTabState: false,
177 },
178};
179
180const thirdDefaultDocument = {
181 path: 'https://apryse.s3.amazonaws.com/public/files/samples/Jupiter_Poster_Raster.png',
182 options: {
183 extension: 'png',
184 filename: 'Jupiter Poster (PNG)',
185 setActive: false,
186 saveCurrentActiveTabState: false,
187 },
188};
189
190// UI section
191//
192// Helper code to add controls to the viewer holding the buttons and dropdown
193// This code creates a container for the buttons and dropdown, styles them, and adds them to the viewer
194
195const buttonNewTab = document.createElement('button');
196buttonNewTab.textContent = 'Add New Tab';
197
198buttonNewTab.onclick = async () => {
199 theInstance.UI.openElements(['OpenFileModal']);
200};
201
202// Create a break element to separate controls into two lines
203const lineBreak = document.createElement('br');
204
205// Create a container for all controls (label, dropdown, and buttons)
206const controlsContainer = document.createElement('div');
207
208buttonNewTab.className = 'btn-style';
209
210controlsContainer.className = 'control-container';
211controlsContainer.appendChild(lineBreak);
212element.insertBefore(controlsContainer, element.firstChild);

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales