Ext JS Tab Panels — Tab Management, Dynamic Tabs, and Tab Customization
In this tutorial, you will learn about Ext JS Tab Panels. We cover key concepts, practical examples, and best practices to help you master this topic.
Ext JS Tab panel organizes content into tabs, supporting dynamic add and remove, tab reordering, closable tabs, icon styling, tab events, and integration with routers for bookmarkable views.
What You'll Learn
- Tab panel configuration and layout
- Dynamic tab creation and removal
- Tab events and tab change handling
- Tab customization (icons, closable, reorderable)
- Tab content Lazy Loading
Why It Matters
Multiview interfaces — settings panels, document editors, dashboard widgets — benefit from tabbed layouts that save screen space while keeping multiple views accessible. Ext JS Tab panel handles tab lifecycle, content management, and layout automatically.
Real-World Use
A document editing application where each open document appears as a tab, tabs show modified indicators, users can reorder tabs by dragging, right-click shows close-all and close-others options, and tab content loads lazily when first selected.
Tab Panel Architecture
flowchart LR
A[Tab Panel] --> B[Tab Bar]
A --> C[Tab Content]
B --> D[Tab 1]
B --> E[Tab 2]
B --> F[Tab 3]
A --> G[Events]
G --> H[tabchange]
G --> I[beforetabchange]
G --> J[tabadd]
G --> K[tabremove]
style A fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
Basic Tab Panel
Ext.create('Ext.tab.Panel', {
title: 'Dashboard',
width: 700,
height: 450,
activeTab: 0,
items: [{
title: 'Overview',
iconCls: 'x-fa fa-dashboard',
html: '<h2>Dashboard Overview</h2><p>Welcome to the dashboard. Select a tab to view details.</p>'
}, {
title: 'Analytics',
iconCls: 'x-fa fa-bar-chart',
html: '<p>Analytics data goes here.</p>',
closable: true
}, {
title: 'Settings',
iconCls: 'x-fa fa-cogs',
html: '<p>Application settings.</p>',
disabled: true
}],
renderTo: Ext.getBody()
});
Expected output: A tab panel with three tabs. The first tab is active by default. The Analytics tab has a close button. The Settings tab is disabled (grayed out, not clickable).
Dynamic Tab Management
var tabPanel = Ext.create('Ext.tab.Panel', {
title: 'Document Editor',
width: 800,
height: 500,
activeTab: 0,
defaults: {
bodyPadding: 10,
closable: true
},
items: [{
title: 'Welcome',
closable: false,
html: '<p>Open a document from the File menu.</p>'
}],
tbar: [{
text: 'New Tab',
handler: function() {
var count = tabPanel.items.length;
tabPanel.add({
title: 'Document ' + count,
html: '<p>Content for document ' + count + '</p>',
closable: true
}).show();
}
}, {
text: 'Close Active',
handler: function() {
var active = tabPanel.getActiveTab();
if (active && active.closable) {
tabPanel.remove(active);
}
}
}, {
text: 'Close All',
handler: function() {
tabPanel.items.each(function(tab) {
if (tab.closable) {
tabPanel.remove(tab);
}
});
}
}],
listeners: {
tabchange: function(tabPanel, newTab, oldTab) {
if (oldTab) {
console.log('Left tab:', oldTab.title);
}
console.log('Activated tab:', newTab.title);
},
remove: function(tabPanel, tab) {
console.log('Closed:', tab.title);
}
},
renderTo: Ext.getBody()
});
Expected output: Clicking "New Tab" adds a closable tab and activates it. "Close Active" removes the current tab. "Close All" removes all closable tabs, keeping the Welcome tab. The tabchange and remove events log to console.
Lazy Loading Tab Content
Ext.create('Ext.tab.Panel', {
title: 'Lazy Load Tabs',
width: 650,
height: 400,
activeTab: 0,
items: [{
title: 'Home',
html: '<p>Home content loads immediately.</p>'
}, {
title: 'Users',
// Lazy load using loader
loader: {
url: '/api/users-tab-content',
autoLoad: false, // Load on first activate
renderer: 'html'
},
listeners: {
activate: function(tab) {
if (!tab.loaded) {
tab.loader.load();
tab.loaded = true;
}
}
}
}, {
title: 'Reports',
// Alternative: Ext.create with deferred render
lazyItems: [{
xtype: 'grid',
title: 'Reports Grid',
// grid config
}],
listeners: {
activate: function(tab) {
if (!tab.rendered) {
tab.add(tab.lazyItems);
tab.rendered = true;
}
}
}
}],
renderTo: Ext.getBody()
});
Expected output: The Home tab loads immediately. The Users tab loads content via Ajax only when first activated. The Reports tab creates its grid component only when first visited.
Tab Customization
Ext.create('Ext.tab.Panel', {
title: 'Custom Tabs',
width: 700,
height: 400,
tabBar: {
// Tab bar position
dock: 'top', // 'top', 'bottom', 'left', 'right'
// Allow reordering
enableTabScroll: true,
// Plain style (no background gradient)
plain: true
},
items: [{
title: 'Email',
iconCls: 'x-fa fa-envelope',
closable: true,
// Tooltip on tab
tooltip: 'Check your inbox',
// Tab config customization
tabConfig: {
style: 'background: #e8f5e9;'
},
html: '<p>Email content</p>'
}, {
title: 'Calendar',
iconCls: 'x-fa fa-calendar',
closable: true,
tabConfig: {
color: 'blue' // Neptune theme supports tab colors
},
html: '<p>Calendar view</p>'
}, {
title: 'Tasks',
iconCls: 'x-fa fa-check-square',
// Badge / notification count
badgeText: '5',
html: '<p>Task list</p>'
}],
// Tab reordering plugin
plugins: [{
ptype: 'tabreorderer'
}],
renderTo: Ext.getBody()
});
Expected output: Tabs show icons, closable buttons, tooltips, and badges. Tab bar is at the top with scroll for overflow. Tabs can be dragged to reorder.
Tab Context Menu
var tabPanel = Ext.create('Ext.tab.Panel', {
title: 'Tab Context Menu',
width: 700,
height: 400,
items: [
{ title: 'Tab 1', html: '<p>Tab 1 content</p>', closable: true },
{ title: 'Tab 2', html: '<p>Tab 2 content</p>', closable: true },
{ title: 'Tab 3', html: '<p>Tab 3 content</p>', closable: true }
],
listeners: {
// Context menu on tab
tabcontextmenu: function(tabPanel, tab, event) {
event.stopEvent();
var menu = Ext.create('Ext.menu.Menu', {
items: [{
text: 'Close Tab',
handler: function() { tabPanel.remove(tab); }
}, {
text: 'Close Others',
handler: function() {
tabPanel.items.each(function(item) {
if (item !== tab && item.closable) {
tabPanel.remove(item);
}
});
}
}, {
text: 'Close All',
handler: function() {
tabPanel.items.each(function(item) {
if (item.closable) { tabPanel.remove(item); }
});
}
}, '-', {
text: 'Reload',
handler: function() { console.log('Reload:', tab.title); }
}]
});
menu.showAt(event.getXY());
}
},
renderTo: Ext.getBody()
});
Expected output: Right-clicking a tab opens a context menu with Close Tab, Close Others, Close All, and Reload options.
Tab Events
Ext.create('Ext.tab.Panel', {
title: 'Tab Events Demo',
width: 700,
height: 400,
items: [
{ title: 'Console', html: '<p>Watch the console for events.</p>' },
{ title: 'Data', html: '<p>Data view</p>' },
{ title: 'Logs', html: '<p>System logs</p>' }
],
listeners: {
beforetabchange: function(tabPanel, newTab, oldTab) {
// Prevent switching if condition not met
if (newTab.title === 'Logs' && !userHasPermission) {
Ext.Msg.alert('Access Denied', 'You do not have permission to view logs.');
return false;
}
},
tabchange: function(tabPanel, newTab) {
// Update URL hash for bookmarking
window.location.hash = newTab.title.toLowerCase();
},
add: function(tabPanel, tab) {
console.log('Tab added:', tab.title);
},
remove: function(tabPanel, tab) {
console.log('Tab removed:', tab.title);
},
enable: function(tab) {
console.log('Tab enabled:', tab.title);
},
disable: function(tab) {
console.log('Tab disabled:', tab.title);
}
},
renderTo: Ext.getBody()
});
Expected output: Switching tabs fires beforetabchange (can cancel), tabchange (updates URL hash), and add/remove events when tabs are added or removed.
Common Mistakes
Not setting activeTab - Without activeTab, all tabs may appear inactive. Set activeTab to the index or reference of the initial active tab.
Destroying tab content without removing from tab panel - Always call tabPanel.remove(tab) instead of directly destroying the tab's content. The remove method handles cleanup.
Loading all tab content upfront - Each tab renders its content when the tab panel renders. For performance, use deferred rendering with activate listeners for tabs that aren't immediately visible.
Forgetting closable: true on dynamic tabs - New tabs added programmatically with items do not show a close button by default. Set closable: true on each tab or in defaults.
Nesting tab panels inside other tab panels - Nested tabs are visually confusing. Use a single tab panel with panels inside each tab, or use window/managed layouts.
Practice Questions
- How do you add a new tab programmatically and make it active?
- What event can prevent a tab switch from happening?
- How do you lazy load tab content only when first activated?
- What is the purpose of the beforetabchange event?
- How do you add a context menu to tabs?
Challenge: Build a tabbed document editor with: a welcome tab (not closable), dynamic document tabs created from a new button, lazy loading content from server on first tab activation, tab context menu (close, close others, close all, rename), tab reordering via drag-and-drop, badge showing unsaved changes, and beforetabchange preventing switching if there are unsaved changes.
FAQ
Mini Project
Build a tabbed admin panel with: a fixed dashboard tab showing summary cards, dynamic tabs for each section (users, products, orders) loaded on first activation, tab context menu with close options, tab badge showing pending counts, drag-to-reorder tabs, and URL hash synchronization with the active tab for browser back/forward support.
What's Next
Tabs organize content in a single view. Learn how Ext JS Windows and Dialogs create modal dialogs, floating Windows, message boxes, and custom popup panels.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro