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