Easily launch and manage multiple documents concurrently with optimized performance across multiple tabs.
This demo allows you to:
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.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
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 DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales