
Syncfusion React Tabs
- 395 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-tabs is an agent skill that implements Syncfusion React Tab components for developers who need multi-section navigation with customizable headers, content strategies, and accessibility in React apps.
About
syncfusion-react-tabs is a component-specific agent skill from syncfusion/react-ui-components-skills that teaches agents to implement the Syncfusion React Tab component correctly. The Tab component organizes related content into tabbed sections with headers, supporting multiple header positions (top, bottom, left, right), overflow handling via scroll or popup modes, flexible content rendering on demand or at initial load, drag-and-drop reordering, ARIA accessibility, animations, HTML sanitization, internationalization, and state persistence across reloads. The skill's SKILL.md documents quick-start examples, key properties, methods, events, and common layout patterns such as vertical left-rail tabs. Install the pack with npx skills add syncfusion/react-ui-components-skills so agents read official APIs instead of guessing props. Developers reach for syncfusion-react-tabs when building settings panels, wizard-style views, or dashboard sections that switch content without route changes. It pairs with other Syncfusion React skills for forms, grids, and charts inside tab panels.
- syncfusion-react-tabs
Syncfusion React Tabs by the numbers
- 395 all-time installs (skills.sh)
- +22 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,057 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-tabsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 395 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you implement Syncfusion React Tabs navigation?
Use syncfusion-react-tabs for development tasks
Who is it for?
React developers adding Syncfusion tabbed navigation to admin panels, settings screens, or multi-view dashboards.
Skip if: Apps using React Router pages alone without Syncfusion or needing only native HTML tab patterns.
When should I use this skill?
A user asks for Syncfusion React Tabs, tabbed settings UI, vertical tabs, or tab overflow handling in React.
What you get
React Tab components with configured headers, panel content, events, animations, and optional persisted active tab state.
- React Tabs component
- Tab header and panel configuration
By the numbers
- Documents Tab header positions at top, bottom, left, and right
Files
Implementing Tabs in Syncfusion React
The Tab component organizes related content into tabbed sections with headers, allowing users to switch between views. This comprehensive skill guides you through setup, customization, content rendering strategies, API methods, events, accessibility, animations, persistence, and advanced features like drag-and-drop reordering.
Table of Contents
- When to Use This Skill
- Component Overview
- Documentation and Navigation Guide
- Quick Start Example
- Common Patterns
- Key Properties Overview
- Methods & API Reference
- Events & Event Arguments
- Animation & Advanced Settings
- Related Skills
When to Use This Skill
- Creating tabbed interfaces with header-based navigation
- Organizing content into logical sections or views
- Building responsive layouts with scrollable or popup overflow handling
- Customizing header styling and icon positioning
- Managing content rendering performance (lazy loading, dynamic, or initial render)
- Implementing drag-and-drop tab reordering for user customization
- Adding localization support for international applications
- Ensuring accessibility compliance (WCAG, ARIA, keyboard navigation)
- Styling tabs with custom CSS or theme customization
- Programmatically managing tabs (add, remove, select, hide/show)
- Handling tab-related events (selection, drag-drop, add, remove)
- Preserving user preferences with state persistence
- Implementing smooth animations and transitions
Component Overview
The Tab component is a navigation control that displays content organized into tabs. Each tab has a header and associated content. Tabs support:
- Multiple header positions (top, bottom, left, right)
- Overflow handling (scrollable navigation or popup mode)
- Flexible content rendering (on-demand, dynamic, or initial load)
- Drag-and-drop reordering for interactive tab management
- Full accessibility with ARIA attributes and keyboard navigation
- Extensive customization via CSS and theme options
- Internationalization through localization APIs
- Animation effects for content transitions
- State persistence across page reloads
- HTML sanitization for security
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Package dependencies and installation
- Vite and Create React App setup
- CSS imports and theme configuration
- Basic tab initialization with JSON items
- Minimal working example and running the app
When to read: Start here to set up a basic Tab component in your React application.
Header Styling & Customization
📄 Read: references/header-styling.md
- Built-in header style classes (e-fill, e-background, e-accent)
- Icon positioning options (left, right, top, bottom)
- Icon and header customization with CSS classes
- Code examples for styled headers
- Styling content and hover states
When to read: Use this when you need to customize tab header appearance, add icons, or apply predefined styles.
Orientation & Overflow Modes
📄 Read: references/orientation-overflow.md
- Header placement positions (top, bottom, left, right)
- Scrollable mode with navigation arrows
- Popup mode with dropdown display
- Touch and swipe support
- Width constraints and responsive behavior
When to read: Use this when you need to control header position, handle many tabs with overflow, or build responsive layouts.
Content Rendering Strategies
📄 Read: references/content-rendering.md
- On-demand rendering (lazy loading, default mode)
- Dynamic rendering for state-isolated tabs
- Initial rendering for state preservation
- Performance vs. state considerations
- Choosing the right rendering mode
When to read: Use this to optimize tab content performance or preserve user interactions across tab switches.
Drag and Drop Reordering
📄 Read: references/drag-drop-reordering.md
- Enabling drag-and-drop with allowDragAndDrop
- Handling drag events (onDragStart, dragging, dragged)
- Preventing drag/drop for specific items
- Tab-to-tab drag and drop
- Dragging tabs to external sources (TreeView)
- Drag area constraints
When to read: Use this to allow users to reorder tabs or integrate tab dragging with other components.
Accessibility & Localization
📄 Read: references/accessibility-localization.md
- WCAG 2.2 and Section 508 compliance
- ARIA attributes and roles for tabs
- Keyboard navigation (arrows, Home, End, Enter, Escape)
- Screen reader support
- Localization with L10n class
- Right-to-left (RTL) language support
When to read: Use this when building accessible applications or supporting multiple languages.
Quick Start Example
Here's a minimal Tab component setup:
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import '@syncfusion/ej2-base/styles/tailwind3.css';
import '@syncfusion/ej2-buttons/styles/tailwind3.css';
import '@syncfusion/ej2-popups/styles/tailwind3.css';
import '@syncfusion/ej2-react-navigations/styles/tailwind3.css';
export default function TabExample() {
const tabItems = [
{
header: { text: 'Home' },
content: 'Welcome to the home tab'
},
{
header: { text: 'Profile' },
content: 'User profile information'
},
{
header: { text: 'Settings' },
content: 'Application settings'
}
];
return (
<TabComponent>
<TabItemsDirective>
{tabItems.map((item, index) => (
<TabItemDirective key={index} header={item.header} content={item.content} />
))}
</TabItemsDirective>
</TabComponent>
);
}Common Patterns
Pattern 1: Styled Tabs with Icons
<TabComponent className="e-fill">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Home', iconCss: 'e-icons e-home' }}
content="Home content"
/>
<TabItemDirective
header={{ text: 'Profile', iconCss: 'e-icons e-user' }}
content="Profile content"
/>
</TabItemsDirective>
</TabComponent>Pattern 2: Vertical Tabs on the Left
<TabComponent headerPlacement="Left">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>Pattern 3: Lazy-Loaded Content (Default)
<TabComponent>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Lazy Tab' }}
content={() => <ExpensiveComponent />}
/>
</TabItemsDirective>
</TabComponent>Pattern 4: Draggable Tabs
<TabComponent allowDragAndDrop={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>Key Properties Overview
| Property | Type | Default | Purpose |
|---|---|---|---|
headerPlacement | String | 'Top' | Position of tab headers: Top, Bottom, Left, Right |
overflowMode | String | 'Scrollable' | Overflow handling: Scrollable or Popup |
loadOn | String | 'Demand' | Content rendering: Demand, Dynamic, or Init |
allowDragAndDrop | Boolean | false | Enable tab reordering via drag and drop |
showCloseButton | Boolean | false | Display close button on tab headers |
locale | String | 'en-US' | Localization culture code (e.g., 'fr-FR') |
width | String/Number | '100%' | Tab container width |
height | String/Number | 'auto' | Tab container height |
heightAdjustMode | String | 'Content' | Height adjustment mode: None, Auto, Content, Fill |
selectedItem | Number | 0 | Index of the active tab |
enablePersistence | Boolean | false | Persist selected tab state across reloads |
enableHtmlSanitizer | Boolean | true | Sanitize HTML content for security |
cssClass | String | '' | Custom CSS classes to apply to component |
enableRtl | Boolean | false | Enable right-to-left layout |
scrollStep | Number | null | Distance to scroll when using scroll arrows |
reorderActiveTab | Boolean | true | Reorder to show active tab in header area |
clearTemplates | Boolean | true | Clear templates when items change dynamically |
dragArea | String | null | Restrict drag area for tab reordering |
swipeMode | String | 'Both' | Swipe detection: Both, Touch, Mouse, None |
animation | TabAnimationSettingsModel | Default | Animation settings for tab transitions |
Methods & API Reference
addTab(items, index)
Adds new items to the Tab component. The items are inserted at the specified index.
Parameters:
items- Array ofTabItemModelobjects to addindex(optional) - Position where items should be inserted. Default is 0.
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AddTabExample() {
const tabRef = useRef(null);
const handleAddTab = () => {
const newItems = [
{
header: { text: 'New Tab' },
content: 'This is dynamically added content'
}
];
// Add at the end (index 2 if there are 2 existing tabs)
tabRef.current.addTab(newItems, 2);
};
return (
<div>
<button onClick={handleAddTab}>Add Tab</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}removeTab(index)
Removes a tab item at the specified index.
Parameters:
index- The index of the tab to remove
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RemoveTabExample() {
const tabRef = useRef(null);
const handleRemoveTab = (tabIndex) => {
tabRef.current.removeTab(tabIndex);
};
return (
<div>
<button onClick={() => handleRemoveTab(1)}>Remove Tab 2</button>
<TabComponent ref={tabRef} showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}select(args, event)
Selects a tab by index or DOM element. Programmatically switches to a different tab.
Parameters:
args- Index (number) or HTMLElement of the tab to selectevent(optional) - DOM event object
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function SelectTabExample() {
const tabRef = useRef(null);
const handleSelectTab = (index) => {
tabRef.current.select(index);
};
return (
<div>
<div>
<button onClick={() => handleSelectTab(0)}>Select Tab 1</button>
<button onClick={() => handleSelectTab(1)}>Select Tab 2</button>
<button onClick={() => handleSelectTab(2)}>Select Tab 3</button>
</div>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}enableTab(index, value)
Enables or disables a specific tab item. Disabled tabs cannot be selected but remain visible.
Parameters:
index- Index of the tab to enable/disablevalue- Boolean.trueto enable,falseto disable
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function EnableTabExample() {
const tabRef = useRef(null);
const toggleTabEnabled = (index) => {
const isCurrentlyEnabled = !tabRef.current.items[index].isDisabled;
tabRef.current.enableTab(index, isCurrentlyEnabled);
};
return (
<div>
<button onClick={() => toggleTabEnabled(1)}>Toggle Tab 2 Enable</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}hideTab(index, value)
Shows or hides a tab item at the specified index. Hidden tabs are removed from the DOM.
Parameters:
index- Index of the tab to show/hidevalue(optional) - Boolean.trueto hide,falseto show. Default istrue
Returns: void
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function HideTabExample() {
const tabRef = useRef(null);
const [visibleTabs, setVisibleTabs] = useState([true, true, true]);
const toggleTabVisibility = (index) => {
const newVisibleTabs = [...visibleTabs];
newVisibleTabs[index] = !newVisibleTabs[index];
setVisibleTabs(newVisibleTabs);
tabRef.current.hideTab(index, !newVisibleTabs[index]);
};
return (
<div>
<div>
<button onClick={() => toggleTabVisibility(0)}>
Toggle Tab 1 Visibility
</button>
<button onClick={() => toggleTabVisibility(1)}>
Toggle Tab 2 Visibility
</button>
</div>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}getItemIndex(tabItemId)
Gets the index of a tab item by its ID.
Parameters:
tabItemId- The ID of the tab item
Returns: number - The index of the tab, or -1 if not found
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function GetItemIndexExample() {
const tabRef = useRef(null);
const findTabIndex = () => {
const itemId = 'tab_2';
const index = tabRef.current.getItemIndex(itemId);
console.log(`Tab with ID '${itemId}' is at index: ${index}`);
};
return (
<div>
<button onClick={findTabIndex}>Find Tab Index</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective id="tab_1" header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective id="tab_2" header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective id="tab_3" header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}refresh()
Refreshes the entire Tab component. Useful after dynamically changing items or properties.
Parameters: None
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RefreshTabExample() {
const tabRef = useRef(null);
const handleRefresh = () => {
tabRef.current.refresh();
console.log('Tab component refreshed');
};
return (
<div>
<button onClick={handleRefresh}>Refresh Tab</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}refreshActiveTab()
Refreshes only the content of the currently active tab. Does not reload other tabs.
Parameters: None
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RefreshActiveTabExample() {
const tabRef = useRef(null);
const handleRefreshActive = () => {
tabRef.current.refreshActiveTab();
console.log('Active tab content refreshed');
};
return (
<div>
<button onClick={handleRefreshActive}>Refresh Active Tab</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Dynamically updated content" />
</TabItemsDirective>
</TabComponent>
</div>
);
}refreshActiveTabBorder()
Refreshes the active tab's visual indicator (underline/border). Useful after styling changes.
Parameters: None
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RefreshBorderExample() {
const tabRef = useRef(null);
const handleRefreshBorder = () => {
tabRef.current.refreshActiveTabBorder();
};
return (
<div>
<button onClick={handleRefreshBorder}>Refresh Border</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}refreshOverflow()
Reorganizes and adjusts the Tab headers to fit the available width without re-rendering the entire component. Useful for responsive layouts.
Parameters: None
Returns: void
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RefreshOverflowExample() {
const tabRef = useRef(null);
const [tabCount, setTabCount] = useState(3);
const handleWindowResize = () => {
if (tabRef.current) {
tabRef.current.refreshOverflow();
}
};
React.useEffect(() => {
window.addEventListener('resize', handleWindowResize);
return () => window.removeEventListener('resize', handleWindowResize);
}, []);
const addMoreTabs = () => {
const newTab = {
header: { text: `Tab ${tabCount + 1}` },
content: `Content ${tabCount + 1}`
};
tabRef.current.addTab([newTab]);
setTabCount(tabCount + 1);
tabRef.current.refreshOverflow();
};
return (
<div>
<button onClick={addMoreTabs}>Add Tab & Refresh Overflow</button>
<TabComponent ref={tabRef} width="100%">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}disable(value)
Disables or enables the entire Tab component.
Parameters:
value- Boolean.trueto disable,falseto enable
Returns: void
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DisableTabExample() {
const tabRef = useRef(null);
const [isDisabled, setIsDisabled] = useState(false);
const toggleDisable = () => {
tabRef.current.disable(!isDisabled);
setIsDisabled(!isDisabled);
};
return (
<div>
<button onClick={toggleDisable}>
{isDisabled ? 'Enable' : 'Disable'} Tab Component
</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}destroy()
Removes the component from the DOM and detaches all event handlers, attributes, and classes.
Parameters: None
Returns: void
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DestroyTabExample() {
const tabRef = useRef(null);
const handleDestroy = () => {
tabRef.current.destroy();
console.log('Tab component destroyed');
};
return (
<div>
<button onClick={handleDestroy}>Destroy Tab</button>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}Events & Event Arguments
The Tab component provides comprehensive event support for handling user interactions and component state changes.
selected
Fired after a tab item is selected. Used to execute actions when the user switches to a different tab.
Event Arguments: SelectEventArgs
SelectEventArgs Properties:
selectedIndex(number) - Index of the newly selected tabselectedItem(HTMLElement) - DOM element of the selected tabselectedContent(HTMLElement) - Content area of the selected tabpreviousIndex(number) - Index of the previously selected tabpreviousItem(HTMLElement) - DOM element of the previous tabisInteracted(boolean) - Whether selection was triggered by user interaction (true) or programmatically (false)isSwiped(boolean) - Whether selection was triggered by swipe gesturepreventFocus(boolean) - Set to true to prevent focus on selected tabcancel(boolean) - Set to true to prevent the selection
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function SelectedEventExample() {
const tabRef = useRef(null);
const [selectedIndex, setSelectedIndex] = useState(0);
const handleTabSelected = (args) => {
console.log('Tab selected:', {
selectedIndex: args.selectedIndex,
previousIndex: args.previousIndex,
isInteracted: args.isInteracted,
isSwiped: args.isSwiped
});
setSelectedIndex(args.selectedIndex);
};
return (
<div>
<p>Currently selected tab index: {selectedIndex}</p>
<TabComponent ref={tabRef} selected={handleTabSelected}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}selecting
Fired before a tab item is selected. Use this to prevent selection under certain conditions.
Event Arguments: SelectingEventArgs (same properties as SelectEventArgs)
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function SelectingEventExample() {
const handleTabSelecting = (args) => {
// Prevent selection of tab at index 1
if (args.selectedIndex === 1) {
console.log('Tab 2 selection prevented');
args.cancel = true;
}
};
return (
<TabComponent selecting={handleTabSelecting}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2 (Locked)' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
);
}added
Fired after a new tab item is added to the component.
Event Arguments: AddEventArgs
AddEventArgs Properties:
addedItems(TabItemModel[]) - Array of added tab itemsname(string) - Name of the eventcancel(boolean) - Set to true to prevent the action
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AddedEventExample() {
const tabRef = useRef(null);
const handleTabAdded = (args) => {
console.log('Tabs added:', args.addedItems.length);
console.log('First added item header:', args.addedItems[0].header.text);
};
const handleAddTab = () => {
const newItems = [
{
header: { text: 'Newly Added Tab' },
content: 'This tab was added dynamically'
}
];
tabRef.current.addTab(newItems);
};
return (
<div>
<button onClick={handleAddTab}>Add Tab</button>
<TabComponent ref={tabRef} added={handleTabAdded}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}adding
Fired before a new tab item is added. Use this to validate or modify items before addition.
Event Arguments: AddEventArgs
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AddingEventExample() {
const tabRef = useRef(null);
const maxTabs = 5;
const handleTabAdding = (args) => {
const totalTabs = tabRef.current.items.length + args.addedItems.length;
if (totalTabs > maxTabs) {
console.log(`Cannot exceed ${maxTabs} tabs`);
args.cancel = true;
}
};
const handleAddTab = () => {
const newItems = [
{
header: { text: `Tab ${tabRef.current.items.length + 1}` },
content: 'New content'
}
];
tabRef.current.addTab(newItems);
};
return (
<div>
<button onClick={handleAddTab}>Add Tab (Max 5)</button>
<TabComponent ref={tabRef} adding={handleTabAdding}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
</div>
);
}removed
Fired after a tab item is removed from the component.
Event Arguments: RemoveEventArgs
RemoveEventArgs Properties:
removedItem(HTMLElement) - DOM element of the removed tabindex(number) - Index of the removed tabname(string) - Name of the event
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RemovedEventExample() {
const tabRef = useRef(null);
const [removedCount, setRemovedCount] = useState(0);
const handleTabRemoved = (args) => {
console.log(`Tab at index ${args.index} was removed`);
setRemovedCount(removedCount + 1);
};
const handleRemoveTab = (index) => {
tabRef.current.removeTab(index);
};
return (
<div>
<p>Tabs removed: {removedCount}</p>
<button onClick={() => handleRemoveTab(0)}>Remove First Tab</button>
<TabComponent ref={tabRef} removed={handleTabRemoved}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}removing
Fired before a tab item is removed. Use this to prevent removal under certain conditions.
Event Arguments: RemoveEventArgs
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RemovingEventExample() {
const handleTabRemoving = (args) => {
const confirmed = window.confirm('Are you sure you want to remove this tab?');
if (!confirmed) {
args.cancel = true;
}
};
return (
<TabComponent removing={handleTabRemoving} showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
);
}onDragStart
Fired before dragging a tab item. Use this to prevent dragging specific tabs or customize drag behavior.
Event Arguments: DragEventArgs
DragEventArgs Properties:
draggedItem(HTMLElement) - The tab being draggedclonedElement(HTMLElement) - Clone of the dragged elementevent(MouseEvent) - The drag eventindex(number) - Index of the dragged tabcancel(boolean) - Set to true to prevent draggingname(string) - Name of the eventtarget(HTMLElement) - Target elementdroppedItem(HTMLElement) - The item being dropped on
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function OnDragStartExample() {
const handleDragStart = (args) => {
// Prevent dragging the first tab (index 0)
if (args.index === 0) {
args.cancel = true;
console.log('First tab cannot be dragged');
}
};
return (
<TabComponent allowDragAndDrop={true} onDragStart={handleDragStart}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1 (Locked)' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
);
}dragging
Fired while a tab is being dragged.
Event Arguments: DragEventArgs
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DraggingEventExample() {
const [dragStatus, setDragStatus] = useState('');
const handleDragging = (args) => {
setDragStatus(`Dragging tab at index ${args.index}`);
};
return (
<div>
<p>Drag status: {dragStatus}</p>
<TabComponent allowDragAndDrop={true} dragging={handleDragging}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}dragged
Fired after a tab has been successfully dropped to a new position.
Event Arguments: DragEventArgs
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DraggedEventExample() {
const [dropMessage, setDropMessage] = useState('');
const handleDragged = (args) => {
setDropMessage(`Tab dropped at new position. Index: ${args.index}`);
console.log('Tab reordered successfully');
};
return (
<div>
<p>{dropMessage}</p>
<TabComponent allowDragAndDrop={true} dragged={handleDragged}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
);
}created
Fired after the Tab component is completely rendered and initialized.
Event Arguments: Event
Example:
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function CreatedEventExample() {
const tabRef = useRef(null);
const handleCreated = () => {
console.log('Tab component created and initialized');
console.log('Total tabs:', tabRef.current.items.length);
};
return (
<TabComponent ref={tabRef} created={handleCreated}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
);
}destroyed
Fired when the Tab component is destroyed and removed from the DOM.
Event Arguments: Event
Example:
import React, { useRef, useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DestroyedEventExample() {
const [showTab, setShowTab] = useState(true);
const handleDestroyed = () => {
console.log('Tab component destroyed');
alert('Tab component has been removed from DOM');
};
return (
<div>
<button onClick={() => setShowTab(!showTab)}>
{showTab ? 'Destroy' : 'Create'} Tab
</button>
{showTab && (
<TabComponent destroyed={handleDestroyed}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
)}
</div>
);
}Animation & Advanced Settings
The Tab component supports advanced features including animations, HTML sanitization, and state persistence.
Animation Configuration
Control how tab content transitions between selections using the animation property.
Animation Settings:
previous- Animation effect when navigating to previous tabnext- Animation effect when navigating to next tab- Each animation has:
effect,duration(ms),easing
Available Animation Effects:
SlideLeftIn- Slide from right to leftSlideRightIn- Slide from left to rightSlideUpIn- Slide from bottom to topSlideDownIn- Slide from top to bottomFadeIn- Fade in effectFadeOut- Fade out effectFadeZoomIn- Fade with zoom inFadeZoomOut- Fade with zoom outZoomIn- Zoom in effectZoomOut- Zoom out effectNone- No animation
Example: Custom Animation
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AnimationExample() {
const tabRef = useRef(null);
const animationSettings = {
previous: { effect: 'SlideLeftIn', duration: 600, easing: 'ease' },
next: { effect: 'SlideRightIn', duration: 600, easing: 'ease' }
};
return (
<TabComponent ref={tabRef} animation={animationSettings}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Twitter' }} content="Twitter is an online social networking service..." />
<TabItemDirective header={{ text: 'Facebook' }} content="Facebook is an online social networking service..." />
<TabItemDirective header={{ text: 'WhatsApp' }} content="WhatsApp Messenger is a proprietary cross-platform instant messaging client..." />
</TabItemsDirective>
</TabComponent>
);
}Example: Disabling Animation
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function NoAnimationExample() {
const animationSettings = {
previous: { effect: 'None' },
next: { effect: 'None' }
};
return (
<TabComponent animation={animationSettings}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
);
}HTML Sanitization
Protect against XSS attacks by enabling HTML sanitization. The enableHtmlSanitizer property controls whether untrusted HTML is sanitized.
Example: HTML Sanitization
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function HtmlSanitizationExample() {
return (
<div>
{/* Sanitization enabled (default) - malicious scripts are removed */}
<h2>With Sanitization (Safe)</h2>
<TabComponent enableHtmlSanitizer={true}>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Safe Content' }}
content="<b>Bold text</b> - This is safe HTML"
/>
<TabItemDirective
header={{ text: 'Dangerous Script' }}
content="<img src=x onerror='alert(\"XSS\")' /> - Script will be removed"
/>
</TabItemsDirective>
</TabComponent>
{/* Sanitization disabled - use only with trusted content */}
<h2>Without Sanitization (Use with Caution)</h2>
<TabComponent enableHtmlSanitizer={false}>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Trusted Content Only' }}
content="<b>Only use when content is from trusted sources</b>"
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}State Persistence
The enablePersistence property allows the Tab component to persist the selected tab across page reloads.
Example: State Persistence
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function PersistenceExample() {
return (
<div>
<p>
Select a different tab and refresh the page.
The previously selected tab will be restored.
</p>
<TabComponent enablePersistence={true} selectedItem={0}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Twitter' }} content="Twitter is an online social networking service that enables users to send and read short 140-character messages..." />
<TabItemDirective header={{ text: 'Facebook' }} content="Facebook is an online social networking service headquartered in Menlo Park, California..." />
<TabItemDirective header={{ text: 'WhatsApp' }} content="WhatsApp Messenger is a proprietary cross-platform instant messaging client for smartphones..." />
</TabItemsDirective>
</TabComponent>
</div>
);
}Persisted State:
selectedItem- The index of the currently active tab
The persistence uses browser local storage to save and restore the state.
Swipe Navigation
The swipeMode property enables or disables tab navigation using swipe gestures. This is particularly useful for mobile devices and touch interfaces, but can be customized to prevent accidental swipes.
SwipeMode Options:
Both(default) - Allows swipe with both touch and mouseTouch- Allows swipe with touch gestures onlyMouse- Allows swipe with mouse gestures onlyNone- Disables swipe navigation completely
Example: Disable Swipe to Prevent Accidental Tab Changes
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function SwipeModeExample() {
return (
<div>
<h3>Form Tab - Swipe Disabled to Prevent Data Loss</h3>
<TabComponent heightAdjustMode='Auto' swipeMode='None'>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Personal Info' }}
content="Fill out your personal information without accidental swipes"
/>
<TabItemDirective
header={{ text: 'Address' }}
content="Enter your address details"
/>
<TabItemDirective
header={{ text: 'Confirmation' }}
content="Review and confirm your information"
/>
</TabItemsDirective>
</TabComponent>
<h3>Mobile Navigation - Touch Swipe Only</h3>
<TabComponent swipeMode='Touch'>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Twitter' }}
content="Twitter content - Swipe on mobile only"
/>
<TabItemDirective
header={{ text: 'Facebook' }}
content="Facebook content - Swipe on mobile only"
/>
<TabItemDirective
header={{ text: 'WhatsApp' }}
content="WhatsApp content - Swipe on mobile only"
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}Example: Disable Swipe for Mouse, Allow Touch
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function TouchSwipeExample() {
return (
<TabComponent swipeMode='Touch'>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Dashboard' }}
content="Dashboard content - Swipe available on touch devices"
/>
<TabItemDirective
header={{ text: 'Analytics' }}
content="Analytics content"
/>
</TabItemsDirective>
</TabComponent>
);
}Tab Scroll Step Customization
The scrollStep property controls the distance (in pixels) that tab headers scroll when you click the left and right navigation arrows. This is useful for fine-tuning the scroll behavior when tabs overflow.
Example: Custom Scroll Step
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function ScrollStepExample() {
const htmlContent = () => {
return <div>
HyperText Markup Language, commonly referred to as HTML, is the standard markup language used to create web pages.
</div>;
};
const csharpContent = () => {
return <div>
C# is intended to be a simple, modern, general-purpose, object-oriented programming language.
</div>;
};
const javaContent = () => {
return <div>
Java is a set of computer software and specifications developed by Sun Microsystems.
</div>;
};
const vbNetContent = () => {
return <div>
The command-line compiler, VBC.EXE, is installed as part of the freeware .NET Framework SDK.
</div>;
};
const xamarinContent = () => {
return <div>
Xamarin is a software company created in May 2011 by the engineers that created Mono.
</div>;
};
const aspNetcontent = () => {
return <div>
ASP.NET is an open-source server-side web application framework designed for web development.
</div>;
};
const mvcContent = () => {
return <div>
The ASP.NET MVC is a web application framework developed by Microsoft.
</div>;
};
const jsContent = () => {
return <div>
JavaScript (JS) is an interpreted computer programming language.
</div>;
};
return (
<div>
<h3>Tab Scroll Step: 50px per click</h3>
<p>Click the left/right navigation arrows - tabs scroll 50 pixels at a time</p>
<TabComponent height="250px" scrollStep={50}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'HTML' }} content={htmlContent} />
<TabItemDirective header={{ text: 'C#' }} content={csharpContent} />
<TabItemDirective header={{ text: 'Java' }} content={javaContent} />
<TabItemDirective header={{ text: 'VB.Net' }} content={vbNetContent} />
<TabItemDirective header={{ text: 'Xamarin' }} content={xamarinContent} />
<TabItemDirective header={{ text: 'ASP.NET' }} content={aspNetcontent} />
<TabItemDirective header={{ text: 'ASP.NET MVC' }} content={mvcContent} />
<TabItemDirective header={{ text: 'JavaScript' }} content={jsContent} />
</TabItemsDirective>
</TabComponent>
<h3>Tab Scroll Step: 100px per click (Faster)</h3>
<p>Larger scroll step for quicker tab navigation</p>
<TabComponent height="250px" scrollStep={100}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'HTML' }} content={htmlContent} />
<TabItemDirective header={{ text: 'C#' }} content={csharpContent} />
<TabItemDirective header={{ text: 'Java' }} content={javaContent} />
<TabItemDirective header={{ text: 'VB.Net' }} content={vbNetContent} />
<TabItemDirective header={{ text: 'Xamarin' }} content={xamarinContent} />
<TabItemDirective header={{ text: 'ASP.NET' }} content={aspNetcontent} />
<TabItemDirective header={{ text: 'ASP.NET MVC' }} content={mvcContent} />
<TabItemDirective header={{ text: 'JavaScript' }} content={jsContent} />
</TabItemsDirective>
</TabComponent>
</div>
);
}Advanced Settings: CSS Classes and RTL
Custom CSS Classes
Apply custom styling using the cssClass property:
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import './CustomTab.css';
export default function CustomCssExample() {
return (
<TabComponent cssClass="custom-tab-theme">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>
);
}RTL (Right-to-Left) Support
Enable RTL layout for Arabic, Hebrew, and other RTL languages:
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function RTLExample() {
return (
<TabComponent enableRtl={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'ملخص' }} content="محتوى في اللغة العربية" />
<TabItemDirective header={{ text: 'تفاصيل' }} content="المزيد من المحتوى" />
<TabItemDirective header={{ text: 'الإعدادات' }} content="إعدادات التطبيق" />
</TabItemsDirective>
</TabComponent>
);
}Combined Advanced Example
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AdvancedSettingsExample() {
const tabRef = useRef(null);
const handleTabSelecting = (args) => {
console.log(`Switching from tab ${args.previousIndex} to tab ${args.selectedIndex}`);
};
const animationSettings = {
previous: { effect: 'SlideLeftIn', duration: 500, easing: 'ease-out' },
next: { effect: 'SlideRightIn', duration: 500, easing: 'ease-out' }
};
return (
<TabComponent
ref={tabRef}
headerPlacement="Top"
overflowMode="Scrollable"
enablePersistence={true}
enableHtmlSanitizer={true}
enableRtl={false}
cssClass="advanced-tab-demo"
animation={animationSettings}
heightAdjustMode="Auto"
selecting={handleTabSelecting}
>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Overview', iconCss: 'e-icons e-home' }}
content="Welcome to the overview tab"
/>
<TabItemDirective
header={{ text: 'Profile', iconCss: 'e-icons e-user' }}
content="User profile information"
/>
<TabItemDirective
header={{ text: 'Settings', iconCss: 'e-icons e-settings' }}
content="Application settings"
/>
</TabItemsDirective>
</TabComponent>
);
}Related Skills
- Implementing Accordion - Similar navigation component for collapsible content
- Implementing Sidebars - Complementary navigation pattern
- Implementing Breadcrumbs - Navigation context alongside tabs
Accessibility & Localization
Table of Contents
- Accessibility Standards
- Keyboard Navigation
- Screen Reader Support
- Localization Setup
- Language Examples
- RTL Support
Accessibility Standards
The Tab component meets comprehensive accessibility requirements:
| Standard | Status | Details |
|---|---|---|
| WCAG 2.2 | ✓ Full Support | Web Content Accessibility Guidelines Level AA |
| Section 508 | ✓ Full Support | U.S. Federal accessibility requirement |
| Screen Readers | ✓ Full Support | Compatible with JAWS, NVDA, VoiceOver |
| Keyboard Navigation | ✓ Full Support | Full keyboard access without mouse |
| Color Contrast | ✓ Full Support | Meets WCAG contrast requirements |
| RTL Languages | ✓ Full Support | Right-to-left layout support |
Keyboard Navigation
The Tab component supports full keyboard navigation following WAI-ARIA Tab Pattern standards.
Keyboard Shortcuts
| Key | Action | Notes |
|---|---|---|
| <kbd>Left Arrow</kbd> | Move to previous tab | First tab has no previous |
| <kbd>Right Arrow</kbd> | Move to next tab | Last tab has no next |
| <kbd>Home</kbd> | Jump to first tab | Useful for long tab lists |
| <kbd>End</kbd> | Jump to last tab | Useful for long tab lists |
| <kbd>Enter</kbd> or <kbd>Space</kbd> | Activate focused tab | Selects tab if not already active |
| <kbd>Tab</kbd> | Move focus to next element | Moves focus away from tabs |
| <kbd>Shift + Tab</kbd> | Move focus to previous element | Moves focus away from tabs |
| <kbd>Escape</kbd> | Close popup (if open) | Only for popup overflow mode |
| <kbd>Shift + F10</kbd> | Open popup | For popup overflow mode |
| <kbd>Delete</kbd> | Delete/close tab | Only if close button enabled |
Keyboard Navigation Example
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function AccessibleTab() {
return (
<div style={{ padding: '20px' }}>
<h1>Keyboard Navigation Example</h1>
<p>
Focus on the tab headers and try these keys:
<ul>
<li><kbd>←</kbd> / <kbd>→</kbd> - Navigate between tabs</li>
<li><kbd>Home</kbd> - Jump to first tab</li>
<li><kbd>End</kbd> - Jump to last tab</li>
<li><kbd>Enter</kbd> - Activate focused tab</li>
</ul>
</p>
<TabComponent>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Dashboard' }}
content="Dashboard content - Use arrow keys to navigate"
/>
<TabItemDirective
header={{ text: 'Analytics' }}
content="Analytics content"
/>
<TabItemDirective
header={{ text: 'Reports' }}
content="Reports content"
/>
<TabItemDirective
header={{ text: 'Settings' }}
content="Settings content"
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}Keyboard Focus Management
The Tab component manages focus automatically:
1. Tab header receives focus first 2. Arrow keys navigate between headers 3. Activating tab keeps focus on header 4. Tab key moves focus out of component 5. Shift + Tab moves focus backward
Screen Reader Support
Screen readers like JAWS, NVDA, and VoiceOver automatically announce:
- Tab list structure: "Tablist, 4 tabs"
- Tab position: "Tab 2 of 4"
- Tab state: "Selected" or "Not selected"
- Tab content: Associated panel content
- Disabled state: If tab is disabled
Example: Screen Reader Announcement
Screen reader announces:
"Tablist, 4 tabs, horizontal"
"Home tab, selected, currently active tab panel displayed"
"Use arrow keys to navigate between tabs"Testing Screen Reader Support
1. Windows NVDA:
- Download: https://www.nvaccess.org/
- Use Num+Plus to start reading
- Test with Firefox or Chrome
2. macOS VoiceOver:
- Enable: System Preferences > Accessibility > VoiceOver
- Start reading: VO (Control+Option) + Right Arrow
- Test with Safari or Chrome
3. Windows JAWS:
- Professional screen reader
- Works with all major browsers
- Announces tab structure automatically
Localization Setup
The Tab component supports localization for close button tooltip text.
L10n Class Setup
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { L10n } from '@syncfusion/ej2-base';
export default function LocalizedTab() {
// Set up localization for French
L10n.load({
'fr-FR': {
'tab': {
'closeButtonTitle': 'Fermer'
}
}
});
return (
<TabComponent
locale="fr-FR"
showCloseButton={true}
>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Accueil' }}
content="Contenu d'accueil"
/>
<TabItemDirective
header={{ text: 'Profil' }}
content="Contenu du profil"
/>
</TabItemsDirective>
</TabComponent>
);
}Locale Property
Set tab language using the locale property:
<TabComponent locale="es-ES">
{/* Spanish tabs */}
</TabComponent>
<TabComponent locale="de-DE">
{/* German tabs */}
</TabComponent>
<TabComponent locale="ja-JP">
{/* Japanese tabs */}
</TabComponent>Language Examples
English (default)
<TabComponent locale="en-US" showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Home' }} content="Welcome" />
</TabItemsDirective>
</TabComponent>Close button tooltip: "Close"
French
import { L10n } from '@syncfusion/ej2-base';
L10n.load({
'fr-FR': {
'tab': {
'closeButtonTitle': 'Fermer'
}
}
});
<TabComponent locale="fr-FR" showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Accueil' }} content="Bienvenue" />
</TabItemsDirective>
</TabComponent>Close button tooltip: "Fermer" (French for Close)
Spanish
L10n.load({
'es-ES': {
'tab': {
'closeButtonTitle': 'Cerrar'
}
}
});
<TabComponent locale="es-ES" showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Inicio' }} content="Bienvenido" />
</TabItemsDirective>
</TabComponent>German
L10n.load({
'de-DE': {
'tab': {
'closeButtonTitle': 'Schließen'
}
}
});
<TabComponent locale="de-DE" showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Startseite' }} content="Willkommen" />
</TabItemsDirective>
</TabComponent>Japanese
L10n.load({
'ja-JP': {
'tab': {
'closeButtonTitle': '閉じる'
}
}
});
<TabComponent locale="ja-JP" showCloseButton={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'ホーム' }} content="ようこそ" />
</TabItemsDirective>
</TabComponent>RTL Support
RTL (Right-to-Left) support for languages like Arabic and Hebrew.
Enable RTL
<TabComponent enableRtl={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'الرئيسية' }} content="محتوى الرئيسية" />
<TabItemDirective header={{ text: 'الملف الشخصي' }} content="محتوى الملف الشخصي" />
</TabItemsDirective>
</TabComponent>RTL with Localization
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { L10n } from '@syncfusion/ej2-base';
export default function ArabicTab() {
// Set up Arabic localization
L10n.load({
'ar-AE': {
'tab': {
'closeButtonTitle': 'إغلاق'
}
}
});
return (
<TabComponent
locale="ar-AE"
enableRtl={true}
showCloseButton={true}
>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'الرئيسية' }}
content="محتوى الرئيسية - رحبا بك"
/>
<TabItemDirective
header={{ text: 'الملف الشخصي' }}
content="معلومات ملفك الشخصي"
/>
<TabItemDirective
header={{ text: 'الإعدادات' }}
content="إعدادات التطبيق"
/>
</TabItemsDirective>
</TabComponent>
);
}RTL Layout Changes
When RTL is enabled:
- Tabs appear right-aligned instead of left-aligned
- Navigation arrows reverse direction
- Content flows right-to-left
- Close buttons align to left
- Vertical tabs on right side (instead of left)
Complete Accessible Example
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { L10n } from '@syncfusion/ej2-base';
export default function AccessibleLocalizedTab() {
// Setup multilingual support
L10n.load({
'fr-FR': { 'tab': { 'closeButtonTitle': 'Fermer' } },
'es-ES': { 'tab': { 'closeButtonTitle': 'Cerrar' } },
'de-DE': { 'tab': { 'closeButtonTitle': 'Schließen' } }
});
const [language, setLanguage] = React.useState('en-US');
const [rtl, setRtl] = React.useState(false);
return (
<div style={{ padding: '20px' }}>
<h1>Accessible and Localized Tab Component</h1>
<div style={{ marginBottom: '20px' }}>
<label>
Language:
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="en-US">English</option>
<option value="fr-FR">Français</option>
<option value="es-ES">Español</option>
<option value="de-DE">Deutsch</option>
</select>
</label>
<label style={{ marginLeft: '20px' }}>
<input
type="checkbox"
checked={rtl}
onChange={(e) => setRtl(e.target.checked)}
/>
Right-to-Left (RTL)
</label>
</div>
<TabComponent
locale={language}
enableRtl={rtl}
showCloseButton={true}
aria-label="Content Navigation Tabs"
>
<TabItemsDirective>
<TabItemDirective
header={{ text: language === 'fr-FR' ? 'Accueil' : 'Home' }}
content="Welcome content with full accessibility"
/>
<TabItemDirective
header={{ text: language === 'fr-FR' ? 'À propos' : 'About' }}
content="About us information"
/>
<TabItemDirective
header={{ text: language === 'fr-FR' ? 'Contact' : 'Contact' }}
content="Contact information"
/>
</TabItemsDirective>
</TabComponent>
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0' }}>
<h3>Accessibility Features</h3>
<ul>
<li>✓ Full WCAG 2.2 compliance</li>
<li>✓ ARIA attributes for screen readers</li>
<li>✓ Keyboard navigation (arrows, Home, End)</li>
<li>✓ Multiple language support</li>
<li>✓ RTL language support</li>
<li>✓ High contrast colors</li>
</ul>
</div>
</div>
);
}Testing Accessibility
1. Keyboard Only: Use the application without mouse 2. Screen Reader: Test with NVDA or similar 3. Color Contrast: Check with WebAIM Contrast Checker 4. Tab Order: Verify logical navigation flow 5. Focus Indicators: Ensure visible focus outline 6. Language Support: Test all localized versions 7. RTL Layout: Verify right-to-left rendering
Best Practices
1. Always provide text labels: Never rely on icons alone 2. Test with actual assistive technologies: Don't assume compliance 3. Use semantic HTML: Leverage ARIA roles properly 4. Maintain keyboard accessibility: Don't require mouse/touch 5. Provide language options: Support international users 6. Test on real devices: Mobile keyboard support varies 7. Color is not the only indicator: Use icons, text, and styling combinations
Content Rendering Strategies
Table of Contents
- Rendering Modes Overview
- On-Demand Rendering (Default)
- Dynamic Rendering
- Initial Rendering
- Choosing the Right Mode
Rendering Modes Overview
The Tab component supports three content rendering strategies, controlled by the loadOn property:
| Mode | Property Value | Content Timing | State | Use Case |
|---|---|---|---|---|
| On-Demand (Lazy Loading) | 'Demand' or omitted | Load when tab selected | Preserved | Default, good balance |
| Dynamic | 'Dynamic' | Load and unload on switch | Not preserved | Performance critical |
| Initial | 'Init' | Load all on component init | Preserved | Few tabs, full access |
On-Demand Rendering (Default)
Mode: Load content only when tab is first selected. Once loaded, content remains in DOM.
How It Works
1. Tab component initializes with only the first tab's content in DOM 2. When user clicks a tab, its content is rendered and added to DOM 3. Content stays in DOM even after switching to another tab 4. Subsequent clicks on same tab show cached content
Configuration
<TabComponent loadOn="Demand">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>Note: loadOn="Demand" is the default. You can omit this property and get the same behavior.Code Example: On-Demand with React Components
import React, { useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { Calendar } from '@syncfusion/ej2-react-calendars';
import { ScheduleComponent, ViewsDirective, ViewDirective, Day, Week, Month, Agenda, Inject } from '@syncfusion/ej2-react-schedule';
export default function OnDemandTab() {
const [changeLog, setChangeLog] = useState([]);
const logChange = (action) => {
setChangeLog(prev => [...prev, `${new Date().toLocaleTimeString()}: ${action}`]);
};
return (
<div style={{ padding: '20px' }}>
<h2>On-Demand (Lazy Loading) - Default Mode</h2>
<TabComponent loadOn="Demand">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Calendar' }}
content={
<div>
<p>Calendar loaded when tab selected</p>
<Calendar />
</div>
}
/>
<TabItemDirective
header={{ text: 'Scheduler' }}
content={
<div>
<p>Scheduler loaded when tab selected</p>
<ScheduleComponent eventSettings={{ dataSource: [] }}>
<ViewsDirective>
<ViewDirective option="Day" />
<ViewDirective option="Week" />
<ViewDirective option="Month" />
</ViewsDirective>
<Inject services={[Day, Week, Month]} />
</ScheduleComponent>
</div>
}
/>
<TabItemDirective
header={{ text: 'Form' }}
content={
<div>
<p>Form loaded when tab selected</p>
<form>
<input type="text" placeholder="Name" />
<input type="email" placeholder="Email" />
</form>
</div>
}
/>
</TabItemsDirective>
</TabComponent>
<div style={{ marginTop: '20px', border: '1px solid #ccc', padding: '10px' }}>
<h3>Change Log</h3>
<ul>
{changeLog.map((log, i) => <li key={i}>{log}</li>)}
</ul>
</div>
</div>
);
}Advantages
- Initial Load: Very fast—only first tab content loaded
- State Preservation: Form inputs, scroll position, component state retained
- Memory: Grows as tabs are visited, but not bloated initially
- Best of Both Worlds: Combines startup speed with state preservation
Disadvantages
- Memory Growth: Memory increases as more tabs are accessed
- Not Ideal For: Many tabs with heavy components (memory could grow large)
When to Use
- Default choice for most applications
- When you need to preserve user state within tabs
- When you have 3-10 tabs
- When content components maintain internal state you want preserved
Performance Consideration
// Memory usage over time (approximate)
// Initial: ~10KB (first tab only)
// After clicking Tab 2: ~20KB
// After clicking Tab 3: ~30KB
// (Memory doesn't decrease when switching back)Dynamic Rendering
Mode: Load and unload content for each tab selection. Only active tab content in DOM.
How It Works
1. Tab component initializes with only first tab's content 2. When user switches tabs, previous tab content is removed from DOM 3. New tab content is rendered and added to DOM 4. Previous content is destroyed and memory freed 5. Clicking same tab again reloads (re-renders) content
Configuration
<TabComponent loadOn="Dynamic">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>Code Example: Dynamic with State-Less Content
import React, { useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DynamicTab() {
const [renderCount, setRenderCount] = useState({});
const getTabContent = (tabName) => {
setRenderCount(prev => ({
...prev,
[tabName]: (prev[tabName] || 0) + 1
}));
return (
<div>
<p>Tab "{tabName}" rendered {renderCount[tabName] || 1} times</p>
<p>This content will be removed when you switch to another tab.</p>
<p>Render count increments each time you visit this tab.</p>
</div>
);
};
return (
<div style={{ padding: '20px' }}>
<h2>Dynamic Rendering Mode</h2>
<p>Each tab content is loaded/unloaded on switch. Switch tabs and watch the render count.</p>
<TabComponent loadOn="Dynamic">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Tab 1' }}
content={getTabContent('Tab 1')}
/>
<TabItemDirective
header={{ text: 'Tab 2' }}
content={getTabContent('Tab 2')}
/>
<TabItemDirective
header={{ text: 'Tab 3' }}
content={getTabContent('Tab 3')}
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}Advantages
- Memory Efficient: Only active tab in DOM, minimal memory footprint
- Clean State: Each tab renders fresh (useful for data collection)
- Performance: Good for many tabs or heavy components
- Scalability: Can handle dozens of tabs without memory concerns
Disadvantages
- State Lost: Form data, scroll position reset on tab switch
- Reload Latency: Slight delay when rendering complex components
- Component Reset: Components lose their internal state
When to Use
- Performance-critical applications
- Many tabs (15+) with complex components
- When state isolation between tabs is desired
- Login workflows where each tab should reset state
- Data collection forms where fresh state is important
Example: Multi-Step Wizard
<TabComponent loadOn="Dynamic">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Step 1: Personal' }}
content={<PersonalForm />}
/>
<TabItemDirective
header={{ text: 'Step 2: Address' }}
content={<AddressForm />}
/>
<TabItemDirective
header={{ text: 'Step 3: Confirm' }}
content={<ConfirmationForm />}
/>
</TabItemsDirective>
</TabComponent>Each step reloads, preventing accidental data carryover between wizard stages.
Initial Rendering
Mode: Render all tab content upfront during component initialization.
How It Works
1. Tab component initializes with ALL tab content rendered and in DOM 2. Clicking tabs switches active display without rendering/unrendering 3. All tab content stays in DOM throughout component lifetime 4. No rendering happens during tab switches
Configuration
<TabComponent loadOn="Init">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>Code Example: Initial Rendering
import React, { useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function InitialTab() {
const [tab1Input, setTab1Input] = useState('');
const [tab2Input, setTab2Input] = useState('');
const [tab3Input, setTab3Input] = useState('');
return (
<div style={{ padding: '20px' }}>
<h2>Initial Rendering Mode</h2>
<p>All tab content loaded upfront. Form data persists as you switch tabs.</p>
<TabComponent loadOn="Init">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Personal Info' }}
content={
<div style={{ padding: '10px' }}>
<label>Name: </label>
<input
type="text"
value={tab1Input}
onChange={(e) => setTab1Input(e.target.value)}
placeholder="Enter name"
/>
<p>Current value: {tab1Input}</p>
</div>
}
/>
<TabItemDirective
header={{ text: 'Email' }}
content={
<div style={{ padding: '10px' }}>
<label>Email: </label>
<input
type="email"
value={tab2Input}
onChange={(e) => setTab2Input(e.target.value)}
placeholder="Enter email"
/>
<p>Current value: {tab2Input}</p>
</div>
}
/>
<TabItemDirective
header={{ text: 'Phone' }}
content={
<div style={{ padding: '10px' }}>
<label>Phone: </label>
<input
type="tel"
value={tab3Input}
onChange={(e) => setTab3Input(e.target.value)}
placeholder="Enter phone"
/>
<p>Current value: {tab3Input}</p>
</div>
}
/>
</TabItemsDirective>
</TabComponent>
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0' }}>
<h3>Summary (All Data Preserved)</h3>
<p>Name: {tab1Input}</p>
<p>Email: {tab2Input}</p>
<p>Phone: {tab3Input}</p>
</div>
</div>
);
}Advantages
- State Preservation: All form data and component state persists
- No Reload: Instant tab switching (no rendering)
- Full Access: Can reference components in other tabs
- Simplicity: No need to manage state across tab switches
Disadvantages
- Initial Load: Slower startup (all content rendered immediately)
- Memory: All content in DOM always (can be large with many tabs)
- Not Scalable: Impractical for 10+ tabs with heavy components
- Performance: DOM grows with each tab
When to Use
- Small number of tabs (3-5)
- Lightweight content
- Forms where all data must be preserved and accessible
- When you need to access components across tabs
- Applications where user needs to see all data at once
Choosing the Right Mode
Decision Matrix
Number of Tabs?
├─ 1-3 tabs → Use "Init" (initial rendering)
├─ 3-10 tabs → Use "Demand" (on-demand, default)
└─ 10+ tabs → Use "Dynamic" (dynamic rendering)
Content Complexity?
├─ Heavy (calendar, scheduler, grid) → Use "Dynamic" or "Demand"
├─ Moderate → Use "Demand"
└─ Light (text, simple forms) → Use any mode
State Preservation Important?
├─ Yes, preserve all data → Use "Init" or "Demand"
└─ No, isolate state per tab → Use "Dynamic"
Performance Priority?
├─ Initial load speed → Use "Dynamic" or "Demand"
├─ Tab switch speed → Use "Init" or "Demand"
└─ Memory efficiency → Use "Dynamic"Common Scenarios
Dashboard with 3-4 tabs (grid, chart, table):
<TabComponent loadOn="Demand"> // Good balanceMulti-step form wizard:
<TabComponent loadOn="Dynamic"> // Fresh state each stepUser profile with all editable fields:
<TabComponent loadOn="Init"> // Preserve all changesFile manager with many categories:
<TabComponent loadOn="Dynamic"> // Many tabs, memory concernPerformance Comparison
Mode | Initial Load | Tab Switch | Memory | State Preserved
-------------|--------------|------------|--------|----------------
Demand | Fast | Instant | Medium | Yes
Dynamic | Fast | Fast | Low | No
Init | Slow | Instant | High | Yes
Example (with 10 tabs of heavy components):
Demand: Initial: 50ms, Switch: instant, Memory: 500MB final
Dynamic: Initial: 50ms, Switch: 100ms, Memory: 50MB
Init: Initial: 500ms, Switch: instant, Memory: 500MB initialDrag and Drop Reordering
Table of Contents
- Basic Drag and Drop
- Drag Events
- Event Handling
- Preventing Drag/Drop
- Drag Constraints
- Tab-to-Tab Drag Drop
- External Source Integration
Basic Drag and Drop
Enable drag-and-drop reordering to allow users to rearrange tab items manually.
Configuration
<TabComponent allowDragAndDrop={true}>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>How It Works
1. User clicks and holds on a tab header 2. Cursor changes to indicate dragging 3. User drags tab to new position 4. Tab reorders when dropped 5. Content automatically updates to match new order
Visual Feedback
- Dragged tab appears semi-transparent/raised
- Drop target position indicated with visual marker
- Tab snaps to final position on release
Example: Simple Draggable Tabs
import React, { useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DraggableTab() {
const [tabs, setTabs] = useState([
{ text: 'Projects', content: 'Active projects list' },
{ text: 'Team', content: 'Team members and roles' },
{ text: 'Settings', content: 'Project settings' },
{ text: 'Archive', content: 'Archived items' }
]);
return (
<div style={{ padding: '20px' }}>
<h2>Draggable Tabs</h2>
<p>Drag tab headers to reorder them</p>
<TabComponent allowDragAndDrop={true}>
<TabItemsDirective>
{tabs.map((tab, index) => (
<TabItemDirective
key={index}
header={{ text: tab.text }}
content={tab.content}
/>
))}
</TabItemsDirective>
</TabComponent>
<div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f0f0f0' }}>
<h3>Current Tab Order:</h3>
<ol>
{tabs.map((tab, i) => <li key={i}>{tab.text}</li>)}
</ol>
</div>
</div>
);
}Drag Events
The Tab component provides events to control and respond to drag operations.
Event Types
| Event | Trigger | Purpose |
|---|---|---|
onDragStart | Before drag begins | Prevent dragging specific items |
dragging | During drag operation | Monitor drag progress |
dragged | After drop completes | Execute post-drop actions |
Event Handling
onDragStart Event
Triggered before a tab starts being dragged. Use to prevent dragging specific tabs.
const handleDragStart = (args) => {
// args.draggableData contains info about dragged item
console.log('Dragging tab:', args.draggableData.text);
// Prevent dragging by setting cancel = true
if (args.draggableData.text === 'Locked Tab') {
args.cancel = true; // Prevent drag
}
};
<TabComponent
allowDragAndDrop={true}
onDragStart={handleDragStart}
>
{/* Tabs */}
</TabComponent>dragging Event
Triggered continuously while dragging. Use to monitor drag progress.
const handleDragging = (args) => {
console.log('Dragging in progress...');
// Can be used for animations or visual feedback
};
<TabComponent
allowDragAndDrop={true}
dragging={handleDragging}
>
{/* Tabs */}
</TabComponent>dragged Event
Triggered after tab is successfully dropped. Use for post-drop actions.
const handleDragged = (args) => {
console.log('Tab dropped at new position');
console.log('Source index:', args.source);
console.log('Target index:', args.target);
// Execute custom logic after reordering
};
<TabComponent
allowDragAndDrop={true}
dragged={handleDragged}
>
{/* Tabs */}
</TabComponent>Complete Event Handling Example
import React, { useState } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function DragEventExample() {
const [eventLog, setEventLog] = useState([]);
const handleDragStart = (args) => {
setEventLog(prev => [...prev, `Start: Dragging "${args.draggableData.text}"`]);
};
const handleDragging = (args) => {
// Only log occasionally to avoid spam
console.log('Dragging...');
};
const handleDragged = (args) => {
setEventLog(prev => [...prev,
`Dropped: Moved from position ${args.source} to ${args.target}`
]);
};
return (
<div style={{ padding: '20px', display: 'flex', gap: '20px' }}>
<div style={{ flex: 1 }}>
<h2>Draggable Tabs with Events</h2>
<TabComponent
allowDragAndDrop={true}
onDragStart={handleDragStart}
dragging={handleDragging}
dragged={handleDragged}
>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Inbox' }} content="Inbox items" />
<TabItemDirective header={{ text: 'Drafts' }} content="Draft emails" />
<TabItemDirective header={{ text: 'Sent' }} content="Sent emails" />
<TabItemDirective header={{ text: 'Spam' }} content="Spam folder" />
</TabItemsDirective>
</TabComponent>
</div>
<div style={{ flex: 1, backgroundColor: '#f0f0f0', padding: '10px' }}>
<h3>Event Log</h3>
<ul style={{ height: '300px', overflow: 'auto' }}>
{eventLog.map((log, i) => (
<li key={i}>{log}</li>
))}
</ul>
<button onClick={() => setEventLog([])}>Clear</button>
</div>
</div>
);
}Preventing Drag/Drop
Prevent Dragging Specific Items
const handleDragStart = (args) => {
// Prevent dragging tabs with "locked" property
if (args.draggableData.locked) {
args.cancel = true;
}
};
<TabComponent
allowDragAndDrop={true}
onDragStart={handleDragStart}
>
{/* Tabs */}
</TabComponent>Prevent Dropping on Specific Positions
const handleDragged = (args) => {
// Revert to original position if dropped on locked tab
if (args.draggableData.locked) {
args.cancel = true; // Reverts the drop
}
};
<TabComponent
allowDragAndDrop={true}
dragged={handleDragged}
>
{/* Tabs */}
</TabComponent>Drag Constraints
dragArea Property
Define the region where dragged tabs can be moved using dragArea property.
<TabComponent
allowDragAndDrop={true}
dragArea=".tab-drag-area"
>
{/* Tabs */}
</TabComponent>Example: Constrained Drag Area
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function ConstrainedDragTab() {
return (
<div style={{ padding: '20px' }}>
<h2>Drag Constrained to Tab Area</h2>
<div
className="tab-drag-area"
style={{
border: '2px dashed #ccc',
padding: '20px',
backgroundColor: '#f9f9f9'
}}
>
<p>Tabs can only be dragged within this bordered area</p>
<TabComponent
allowDragAndDrop={true}
dragArea=".tab-drag-area"
>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>
</div>
<p style={{ marginTop: '20px' }}>
Tabs cannot be dragged above or below the dashed border.
</p>
</div>
);
}Tab-to-Tab Drag Drop
Transfer tabs between two Tab components by dragging.
How It Works
1. Enable allowDragAndDrop on both Tab components 2. Implement onDragStart to store dragged tab info 3. Implement dragged event to transfer tab to target Tab 4. Use addTab() and removeTab() methods
Example: Tab Transfer Between Two Containers
import React, { useState, useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function TabToTabDragDrop() {
const [tab1Items, setTab1Items] = useState([
{ text: 'Tab A', content: 'Content A' },
{ text: 'Tab B', content: 'Content B' }
]);
const [tab2Items, setTab2Items] = useState([
{ text: 'Tab 1', content: 'Content 1' },
{ text: 'Tab 2', content: 'Content 2' }
]);
const tab1Ref = useRef(null);
const tab2Ref = useRef(null);
const draggedTabRef = useRef(null);
const handleDragStart1 = (args) => {
draggedTabRef.current = {
item: args.draggableData,
source: 'tab1',
index: args.index
};
};
const handleDragStart2 = (args) => {
draggedTabRef.current = {
item: args.draggableData,
source: 'tab2',
index: args.index
};
};
const handleDragged1 = (args) => {
const draggedInfo = draggedTabRef.current;
if (draggedInfo && draggedInfo.source === 'tab2') {
// Add to tab1
const newItem = tab2Items[draggedInfo.index];
setTab1Items(prev => [...prev, newItem]);
// Remove from tab2
setTab2Items(prev => prev.filter((_, i) => i !== draggedInfo.index));
}
};
const handleDragged2 = (args) => {
const draggedInfo = draggedTabRef.current;
if (draggedInfo && draggedInfo.source === 'tab1') {
// Add to tab2
const newItem = tab1Items[draggedInfo.index];
setTab2Items(prev => [...prev, newItem]);
// Remove from tab1
setTab1Items(prev => prev.filter((_, i) => i !== draggedInfo.index));
}
};
return (
<div style={{ padding: '20px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
<div>
<h3>Tab Container 1</h3>
<p>Drag tabs to Container 2</p>
<TabComponent
ref={tab1Ref}
allowDragAndDrop={true}
onDragStart={handleDragStart1}
dragged={handleDragged1}
>
<TabItemsDirective>
{tab1Items.map((item, index) => (
<TabItemDirective
key={index}
header={{ text: item.text }}
content={item.content}
/>
))}
</TabItemsDirective>
</TabComponent>
</div>
<div>
<h3>Tab Container 2</h3>
<p>Drag tabs to Container 1</p>
<TabComponent
ref={tab2Ref}
allowDragAndDrop={true}
onDragStart={handleDragStart2}
dragged={handleDragged2}
>
<TabItemsDirective>
{tab2Items.map((item, index) => (
<TabItemDirective
key={index}
header={{ text: item.text }}
content={item.content}
/>
))}
</TabItemsDirective>
</TabComponent>
</div>
</div>
);
}External Source Integration
Drag Tabs to TreeView
Transfer tabs to a TreeView component:
import React, { useState, useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { TreeViewComponent } from '@syncfusion/ej2-react-navigations';
export default function TabToTreeViewDragDrop() {
const [tabItems, setTabItems] = useState([
{ text: 'Folder 1', content: 'Content 1' },
{ text: 'Folder 2', content: 'Content 2' },
{ text: 'Folder 3', content: 'Content 3' }
]);
const [treeItems, setTreeItems] = useState([
{ id: '1', text: 'Archives', expanded: true, children: [] }
]);
const draggedTabRef = useRef(null);
const handleDragStart = (args) => {
draggedTabRef.current = {
item: args.draggableData,
index: args.index
};
};
const handleDragged = (args) => {
const draggedInfo = draggedTabRef.current;
if (draggedInfo) {
// Add to TreeView
const newNode = {
id: Date.now().toString(),
text: draggedInfo.item.text
};
setTreeItems(prev => {
const updated = JSON.parse(JSON.stringify(prev));
if (updated[0].children) {
updated[0].children.push(newNode);
}
return updated;
});
// Remove from Tab
setTabItems(prev => prev.filter((_, i) => i !== draggedInfo.index));
}
};
return (
<div style={{ padding: '20px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
<div>
<h3>Tab Component</h3>
<p>Drag tabs to TreeView below</p>
<TabComponent
allowDragAndDrop={true}
onDragStart={handleDragStart}
dragged={handleDragged}
>
<TabItemsDirective>
{tabItems.map((item, index) => (
<TabItemDirective
key={index}
header={{ text: item.text }}
content={item.content}
/>
))}
</TabItemsDirective>
</TabComponent>
</div>
<div>
<h3>TreeView (Drop Target)</h3>
<TreeViewComponent fields={{ dataSource: treeItems }} />
</div>
</div>
);
}Best Practices
1. Provide Visual Feedback: Use event handlers to show drag status 2. Set Constraints: Use dragArea to prevent unintended drops 3. Handle Edge Cases:
- Prevent dragging disabled tabs
- Validate drop targets
- Handle empty tab containers
4. User Guidance: Add instructions or help text about drag functionality 5. Mobile Considerations: Ensure drag works on touch devices; test thoroughly 6. Accessibility: Provide keyboard alternatives to drag-and-drop
Getting Started with Tabs
Table of Contents
Dependencies
The Tab component requires the @syncfusion/ej2-react-navigations package and its dependencies. The complete dependency tree is:
@syncfusion/ej2-react-navigations
├── @syncfusion/ej2-base
├── @syncfusion/ej2-react-base
└── @syncfusion/ej2-navigations
├── @syncfusion/ej2-buttons
└── @syncfusion/ej2-popupsInstall the required package:
npm install @syncfusion/ej2-react-navigations --saveThis single command installs all required dependencies automatically.
Project Setup
Option 1: Setup with Vite (Recommended)
Vite provides faster development builds and smaller bundle sizes compared to Create React App.
Create a new React application:
npm create vite@latest my-tab-app -- --template react
cd my-tab-app
npm run devFor TypeScript support:
npm create vite@latest my-tab-app -- --template react-ts
cd my-tab-app
npm run devInstall Syncfusion Tab package:
npm install @syncfusion/ej2-react-navigations --saveOption 2: Setup with Create React App
If you prefer Create React App, follow the official setup:
npx create-react-app my-tab-app
cd my-tab-app
npm install @syncfusion/ej2-react-navigations --save
npm startFor detailed Create React App setup, refer to the official Create React App documentation.
CSS Imports
The Tab component requires CSS files for styling. Add these imports to your src/App.css file:
@import '../node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import '../node_modules/@syncfusion/ej2-react-navigations/styles/tailwind3.css';Then import the CSS in your src/App.tsx (or src/App.jsx):
import './App.css';Why these imports?
- ej2-base: Core Syncfusion styles and utilities
- ej2-buttons: Required for button styling in Tab icons
- ej2-popups: Required for dropdown/popup overflow functionality
- ej2-react-navigations: Tab-specific component styles
Basic Tab Initialization
Step 1: Import Tab Components
In your src/App.tsx file, import the necessary components:
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import './App.css';Step 2: Create Tab Items
Define your tab data structure with headers and content:
const tabItems = [
{
header: { text: 'Home' },
content: 'Welcome to the home tab. You can add any content here.'
},
{
header: { text: 'Profile' },
content: 'User profile information goes here.'
},
{
header: { text: 'Settings' },
content: 'Application settings and preferences.'
}
];Step 3: Render the Tab Component
Create your React component:
export default function App() {
const tabItems = [
{
header: { text: 'Home' },
content: 'Welcome to the home tab'
},
{
header: { text: 'Profile' },
content: 'User profile information'
},
{
header: { text: 'Settings' },
content: 'Application settings'
}
];
return (
<div className="App">
<h1>Tab Component Example</h1>
<TabComponent>
<TabItemsDirective>
{tabItems.map((item, index) => (
<TabItemDirective
key={index}
header={item.header}
content={item.content}
/>
))}
</TabItemsDirective>
</TabComponent>
</div>
);
}Complete App.tsx Example
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import '@syncfusion/ej2-base/styles/tailwind3.css';
import '@syncfusion/ej2-buttons/styles/tailwind3.css';
import '@syncfusion/ej2-popups/styles/tailwind3.css';
import '@syncfusion/ej2-react-navigations/styles/tailwind3.css';
import './App.css';
export default function App() {
const tabItems = [
{
header: { text: 'HTML' },
content: 'HTML is the standard markup language for creating web pages.'
},
{
header: { text: 'CSS' },
content: 'CSS is used for styling and layout of web pages.'
},
{
header: { text: 'JavaScript' },
content: 'JavaScript is a programming language for interactive web pages.'
}
];
return (
<div style={{ padding: '20px' }}>
<h1>My First Tab Component</h1>
<TabComponent>
<TabItemsDirective>
{tabItems.map((item, index) => (
<TabItemDirective
key={index}
header={item.header}
content={item.content}
/>
))}
</TabItemsDirective>
</TabComponent>
</div>
);
}Running the Application
With Vite:
npm run devOutput:
VITE v5.0.0 ready in 123 ms
➜ Local: http://localhost:5173/
➜ press h to show helpOpen your browser to http://localhost:5173/ to see your Tab component.
With Create React App:
npm startThe app will automatically open in your default browser at http://localhost:3000/.
Minimal Working Example (MWE)
If you want the absolute minimum code to get started:
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import '@syncfusion/ej2-react-navigations/styles/tailwind3.css';
export default function App() {
return (
<TabComponent>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content='Content 1' />
<TabItemDirective header={{ text: 'Tab 2' }} content='Content 2' />
</TabItemsDirective>
</TabComponent>
);
}This renders a basic Tab component with two tabs and default styling.
Next Steps
- For styling: Read the header-styling.md guide to customize tab appearance
- For responsive layouts: Read orientation-overflow.md to control header position and overflow handling
- For advanced content: Read content-rendering.md to understand performance implications of different rendering modes
Header Styling & Customization
Table of Contents
Built-In Header Styles
The Tab component provides predefined CSS classes to style tab headers. Apply these classes directly to the TabComponent element.
Style Class: e-fill
Effect: Selected tab header background is solid fill with highlight
<TabComponent className="e-fill">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>When to use: For prominent active tab indication with solid background color.
Style Class: e-background
Effect: Tab header has solid fill background, selected header has highlighted border
<TabComponent className="e-background">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>When to use: For subtle tab indication with border highlighting.
Style Class: e-background e-accent
Effect: Tab header has solid fill background, selected header highlighted with accent color
<TabComponent className="e-background e-accent">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>When to use: For branded or accent-colored tab highlighting. Most visually distinctive option.
Default Style (No Class)
Without any class, the Tab component uses default styling with underline indication for active tabs.
<TabComponent>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
</TabItemsDirective>
</TabComponent>When to use: For minimal, clean interface with simple underline styling.
Icon Positioning
Each tab header item can position its icon using the iconPosition property defined within the header model. This property works in conjunction with the iconCss property to customize icon placement.
Icon Positions
| Position | Description | Use Case |
|---|---|---|
Left | Icon on the left of text (default) | Standard icon-text layout |
Right | Icon on the right of text | Right-aligned icon design |
Top | Icon above text | Vertical icon-text stacking |
Bottom | Icon below text | Icon below label design |
Example: Icon Position Configuration (Left)
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function IconPositionLeftExample() {
return (
<TabComponent>
<TabItemsDirective>
<TabItemDirective
header={{
text: 'Home',
iconCss: 'e-icons e-home',
iconPosition: 'Left'
}}
content="Home content"
/>
<TabItemDirective
header={{
text: 'Profile',
iconCss: 'e-icons e-user',
iconPosition: 'Left'
}}
content="Profile content"
/>
<TabItemDirective
header={{
text: 'Settings',
iconCss: 'e-icons e-settings',
iconPosition: 'Left'
}}
content="Settings content"
/>
</TabItemsDirective>
</TabComponent>
);
}Icon Position: Top
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function IconPositionTopExample() {
return (
<TabComponent>
<TabItemsDirective>
<TabItemDirective
header={{
text: 'Dashboard',
iconCss: 'e-icons e-dashboard',
iconPosition: 'Top'
}}
content="Dashboard content"
/>
<TabItemDirective
header={{
text: 'Analytics',
iconCss: 'e-icons e-chart-pie',
iconPosition: 'Top'
}}
content="Analytics content"
/>
</TabItemsDirective>
</TabComponent>
);
}Icon Position: Dynamic Change
import React, { useRef } from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
export default function DynamicIconPositionExample() {
const tabRef = useRef(null);
const dropRef = useRef(null);
const handlePositionChange = () => {
const newPosition = dropRef.current.value;
const items = tabRef.current.items;
for (let i = 0; i < items.length; i++) {
items[i].header.iconPosition = newPosition;
}
};
const positionOptions = [
{ text: 'Left', value: 'Left' },
{ text: 'Right', value: 'Right' },
{ text: 'Top', value: 'Top' },
{ text: 'Bottom', value: 'Bottom' }
];
return (
<div>
<label>Select Icon Position:</label>
<DropDownListComponent
ref={dropRef}
dataSource={positionOptions}
fields={{ text: 'text', value: 'value' }}
value="Left"
change={handlePositionChange}
/>
<TabComponent ref={tabRef}>
<TabItemsDirective>
<TabItemDirective
header={{
text: 'Messages',
iconCss: 'e-icons e-message',
iconPosition: 'Left'
}}
content="Messages content"
/>
<TabItemDirective
header={{
text: 'Users',
iconCss: 'e-icons e-people',
iconPosition: 'Left'
}}
content="Users content"
/>
<TabItemDirective
header={{
text: 'Settings',
iconCss: 'e-icons e-settings',
iconPosition: 'Left'
}}
content="Settings content"
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}Icon Customization
Use the iconCss property in the header object to specify custom icon CSS classes.
Available Icon Classes
Syncfusion provides icon classes prefixed with e-icons e-. Common examples:
e-icons e-home- Home icone-icons e-user- User/Profile icone-icons e-settings- Settings icone-icons e-mail- Mail/Message icone-icons e-chart-pie- Chart icone-icons e-dashboard- Dashboard icone-icons e-folder- Folder icone-icons e-file- File icon
Example: Icon-Heavy Tab
<TabComponent className="e-fill" iconPosition="Top">
<TabItemsDirective>
<TabItemDirective
header={{
text: 'Dashboard',
iconCss: 'e-icons e-dashboard'
}}
content="Dashboard metrics and analytics"
/>
<TabItemDirective
header={{
text: 'Users',
iconCss: 'e-icons e-people'
}}
content="User management"
/>
<TabItemDirective
header={{
text: 'Reports',
iconCss: 'e-icons e-report'
}}
content="Report generation and viewing"
/>
<TabItemDirective
header={{
text: 'Settings',
iconCss: 'e-icons e-settings'
}}
content="System configuration"
/>
</TabItemsDirective>
</TabComponent>Custom Styling
CSS Selectors for Tab Components
Beyond built-in classes, you can apply custom CSS:
Tab container:
.e-tab {
border: 5px solid rgb(173, 255, 47);
}Tab header section:
.e-tab .e-tab-header {
background: #badfba !important;
}Tab header items/toolbar:
.e-tab .e-tab-header .e-toolbar-items {
background: #9faed8;
border: 2px solid blue;
}Tab header icons:
.e-tab .e-tab-header .e-toolbar-item .e-tab-icon {
color: #badfba !important;
}Tab content section:
.e-tab .e-content {
background: #d1f6d1 !important;
}Tab content items:
.e-tab .e-content .e-item {
color: #a78515;
font-size: 14px;
}Tab hover state:
.e-tab .e-tab-header .e-toolbar-item .e-tab-wrap:hover {
background: #d1f6d1 !important;
}Tab popup icon (overflow):
.e-tab .e-tab-header .e-tab-wrap .e-popup-icon:hover {
background: #d1f6d1 !important;
}Styling Examples
Example 1: Modern Professional Style
import React from 'react';
import { TabComponent, TabItemsDirective, TabItemDirective } from '@syncfusion/ej2-react-navigations';
export default function ProfessionalTab() {
return (
<div style={{ padding: '20px' }}>
<style>{`
.professional-tab .e-tab-header {
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.professional-tab .e-tab-header .e-toolbar-item .e-tab-text {
color: white;
font-weight: 500;
padding: 12px 20px;
}
.professional-tab .e-tab-header .e-toolbar-item.e-active .e-tab-text {
border-bottom: 3px solid white;
}
.professional-tab .e-content {
background: #f8f9fa;
padding: 20px;
}
`}</style>
<TabComponent className="professional-tab">
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Overview' }}
content="Account overview and quick statistics"
/>
<TabItemDirective
header={{ text: 'Activity' }}
content="Recent activity and logs"
/>
<TabItemDirective
header={{ text: 'Settings' }}
content="Account settings and preferences"
/>
</TabItemsDirective>
</TabComponent>
</div>
);
}Example 2: Icon Tabs with Background
<TabComponent className="e-background e-accent" iconPosition="Top">
<TabItemsDirective>
<TabItemDirective
header={{
text: 'Orders',
iconCss: 'e-icons e-cart'
}}
content="Order management and history"
/>
<TabItemDirective
header={{
text: 'Payments',
iconCss: 'e-icons e-money'
}}
content="Payment methods and history"
/>
<TabItemDirective
header={{
text: 'Shipments',
iconCss: 'e-icons e-shipping'
}}
content="Shipment tracking and status"
/>
</TabItemsDirective>
</TabComponent>Example 3: Minimal Clean Style
<TabComponent>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Description' }}
content="Product description goes here"
/>
<TabItemDirective
header={{ text: 'Reviews' }}
content="Customer reviews section"
/>
<TabItemDirective
header={{ text: 'Specifications' }}
content="Technical specifications"
/>
</TabItemsDirective>
</TabComponent>Best Practices
1. Choose style class based on importance: Use e-fill for primary navigation, default for secondary 2. Icon positioning: Use Left or Right for horizontal layouts, Top or Bottom for compact designs 3. Consistency: Apply consistent styling across all tabs in your application 4. Accessibility: Ensure sufficient color contrast and don't rely on color alone to indicate state 5. Performance: Use CSS classes instead of inline styles for better performance and maintainability
Orientation & Overflow Modes
Table of Contents
Header Placement Positions
Control where tab headers appear relative to content using the headerPlacement property.
Position: Top (Default)
Headers are arranged horizontally above the content.
<TabComponent headerPlacement="Top">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>Use cases:
- Standard navigation layouts
- Web applications with horizontal navigation
- Desktop interfaces
Position: Bottom
Headers are arranged horizontally below the content.
<TabComponent headerPlacement="Bottom">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Tab 1' }} content="Content 1" />
<TabItemDirective header={{ text: 'Tab 2' }} content="Content 2" />
<TabItemDirective header={{ text: 'Tab 3' }} content="Content 3" />
</TabItemsDirective>
</TabComponent>Use cases:
- Bottom navigation bars
- Mobile-style navigation
- Status indicators at bottom
Position: Left
Headers are arranged vertically on the left side, content on the right.
<TabComponent headerPlacement="Left">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Dashboard' }} content="Dashboard content" />
<TabItemDirective header={{ text: 'Users' }} content="Users content" />
<TabItemDirective header={{ text: 'Reports' }} content="Reports content" />
<TabItemDirective header={{ text: 'Settings' }} content="Settings content" />
</TabItemsDirective>
</TabComponent>Use cases:
- Sidebar navigation layouts
- Wide-screen applications
- Multi-level navigation menus
Position: Right
Headers are arranged vertically on the right side, content on the left.
<TabComponent headerPlacement="Right">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Overview' }} content="Overview content" />
<TabItemDirective header={{ text: 'Details' }} content="Details content" />
<TabItemDirective header={{ text: 'History' }} content="History content" />
</TabItemsDirective>
</TabComponent>Use cases:
- Right-to-left (RTL) language layouts
- Right-side navigation panels
- Asymmetric design patterns
Overflow Modes
When tab headers exceed the available space, the component handles overflow using different modes.
Mode: Scrollable (Default)
How it works:
- Single-line horizontal display with navigation arrows
- Left/right arrow buttons at start/end of header
- Click or hold arrow to scroll through tabs
- Touch/swipe support on mobile devices
<TabComponent overflowMode="Scrollable">
<TabItemsDirective>
{Array.from({ length: 20 }, (_, i) => (
<TabItemDirective
key={i}
header={{ text: `Tab ${i + 1}` }}
content={`Content ${i + 1}`}
/>
))}
</TabItemsDirective>
</TabComponent>Visual behavior:
- Left navigation arrow initially disabled
- Right arrow enabled when tabs overflow right
- Arrows toggle based on scroll position
Mobile behavior:
- No navigation arrows on touch devices
- Swipe left/right to scroll through tabs
- Smooth momentum scrolling
Advantages:
- All tab headers visible (with navigation)
- Compact vertical footprint
- Intuitive scrolling interaction
Disadvantages:
- May be slow to reach tabs far to the right
- Navigation arrows take screen space
Mode: Popup
How it works:
- Only tabs fitting available space are displayed
- Overflow tabs moved to dropdown menu
- Dropdown icon at header end to view hidden tabs
- Scrollable dropdown if many tabs exceed visible area
<TabComponent overflowMode="Popup">
<TabItemsDirective>
{Array.from({ length: 20 }, (_, i) => (
<TabItemDirective
key={i}
header={{ text: `Tab ${i + 1}` }}
content={`Content ${i + 1}`}
/>
))}
</TabItemsDirective>
</TabComponent>Visual behavior:
- Visible tabs displayed in header
- Dropdown icon appears when tabs overflow
- Click dropdown to see hidden tabs
- Popup scrolls if many hidden tabs
Mobile behavior:
- Same dropdown interaction
- Tap dropdown arrow to show hidden tabs
- Scrollable list if dropdown exceeds screen height
Advantages:
- Cleaner header appearance
- Fast access to any tab via dropdown
- Compact for many tabs
Disadvantages:
- Hidden tabs not immediately visible
- Extra click/tap needed to access hidden tabs
Scrollable Mode
Configuration
<TabComponent
overflowMode="Scrollable"
width="800px"
height="300px"
>
<TabItemsDirective>
{/* Tab items */}
</TabItemsDirective>
</TabComponent>Navigation Control
Programmatic scrolling:
import React, { useRef } from 'react';
export default function ScrollableTabExample() {
const tabRef = useRef(null);
const handleScroll = (direction) => {
// Tab component provides scroll methods
// Use tabRef.current to access component
};
return (
<div>
<button onClick={() => handleScroll('left')}>← Scroll Left</button>
<button onClick={() => handleScroll('right')}>Scroll Right →</button>
<TabComponent ref={tabRef} overflowMode="Scrollable">
<TabItemsDirective>
{/* Tabs */}
</TabItemsDirective>
</TabComponent>
</div>
);
}Touch and Swipe Support
Scrollable mode automatically supports touch gestures:
- Swipe left: Scroll tabs to right
- Swipe right: Scroll tabs to left
- Momentum scrolling: Smooth deceleration after swipe
No additional configuration needed—works automatically on touch devices.
Popup Mode
Dropdown Behavior
When overflowMode="Popup", hidden tabs appear in a dropdown:
<TabComponent overflowMode="Popup">
<TabItemsDirective>
<TabItemDirective header={{ text: 'Home' }} content="Home content" />
<TabItemDirective header={{ text: 'About' }} content="About content" />
{/* More tabs that may overflow */}
</TabItemsDirective>
</TabComponent>Dropdown features:
- Click dropdown arrow to open/close menu
- Keyboard navigation in dropdown (arrow keys)
- Click tab in dropdown to switch to it
- Dropdown closes after tab selection
Dropdown Scrolling
If many tabs overflow:
<TabComponent
overflowMode="Popup"
width="600px"
>
<TabItemsDirective>
{Array.from({ length: 50 }, (_, i) => (
<TabItemDirective
key={i}
header={{ text: `Tab ${i + 1}` }}
content={`Content ${i + 1}`}
/>
))}
</TabItemsDirective>
</TabComponent>Behavior:
- First tabs fit in header area
- Remaining tabs scroll in popup dropdown
- Dropdown scrollbar appears automatically if needed
Responsive Configuration
Width and Height
Control tab container dimensions:
<TabComponent
width="100%"
height="400px"
overflowMode="Scrollable"
>
<TabItemsDirective>
{/* Tabs */}
</TabItemsDirective>
</TabComponent>Valid values:
- Pixel values:
"800px","400px" - Percentage:
"100%","50%" - CSS units:
"20em","5rem"
Dynamic Width Adjustment
import React, { useState } from 'react';
export default function ResponsiveTab() {
const [containerWidth, setContainerWidth] = useState('100%');
return (
<div>
<button onClick={() => setContainerWidth('600px')}>Narrow</button>
<button onClick={() => setContainerWidth('100%')}>Full Width</button>
<TabComponent
width={containerWidth}
overflowMode="Scrollable"
>
<TabItemsDirective>
{/* Tabs */}
</TabItemsDirective>
</TabComponent>
</div>
);
}Vertical Overflow (Left/Right Placement)
When using headerPlacement="Left" or "Right", overflow is vertical:
<TabComponent
headerPlacement="Left"
overflowMode="Scrollable"
height="300px"
>
<TabItemsDirective>
{Array.from({ length: 20 }, (_, i) => (
<TabItemDirective
key={i}
header={{ text: `Tab ${i + 1}` }}
content={`Content ${i + 1}`}
/>
))}
</TabItemsDirective>
</TabComponent>Behavior:
- Headers arranged vertically
- Up/down navigation arrows for overflow
- Swipe up/down on mobile
Real-World Examples
Example 1: Full-Width Responsive Tabs
<TabComponent
width="100%"
overflowMode="Scrollable"
headerPlacement="Top"
>
<TabItemsDirective>
<TabItemDirective header={{ text: 'Products' }} content="Products catalog" />
<TabItemDirective header={{ text: 'Pricing' }} content="Pricing information" />
<TabItemDirective header={{ text: 'Features' }} content="Feature comparison" />
<TabItemDirective header={{ text: 'Documentation' }} content="Docs and guides" />
<TabItemDirective header={{ text: 'Support' }} content="Support resources" />
</TabItemsDirective>
</TabComponent>Example 2: Sidebar Navigation with Vertical Tabs
<div style={{ display: 'flex', height: '100vh' }}>
<TabComponent
headerPlacement="Left"
overflowMode="Scrollable"
height="100%"
width="250px"
>
<TabItemsDirective>
<TabItemDirective
header={{ text: 'Dashboard', iconCss: 'e-icons e-dashboard' }}
content="Dashboard metrics"
/>
<TabItemDirective
header={{ text: 'Users', iconCss: 'e-icons e-people' }}
content="User management"
/>
{/* More tabs */}
</TabItemsDirective>
</TabComponent>
<div style={{ flex: 1, padding: '20px' }}>
{/* Main content */}
</div>
</div>Example 3: Compact Popup Dropdown
<TabComponent
overflowMode="Popup"
width="500px"
className="e-background"
>
<TabItemsDirective>
{Array.from({ length: 15 }, (_, i) => (
<TabItemDirective
key={i}
header={{ text: `Section ${i + 1}` }}
content={`Content for section ${i + 1}`}
/>
))}
</TabItemsDirective>
</TabComponent>Best Practices
1. Choose mode based on tab count:
- Scrollable for up to 10-12 tabs
- Popup for 10+ tabs to keep header clean
2. Use appropriate placement:
- Top: Standard horizontal navigation
- Left: Sidebar layouts for multi-section apps
- Bottom: Mobile-friendly or tab toolbars
3. Set reasonable container width:
- Allow tabs to fit naturally
- Avoid forcing overflow on desktop
- Use responsive sizing (100%) for flexibility
4. Test on multiple devices:
- Desktop mouse/arrow interactions
- Mobile swipe/touch interactions
- Tablet intermediate sizes
Related skills
How it compares
Use syncfusion-react-tabs inside Syncfusion design systems; use headless UI tab primitives when avoiding commercial component licenses.
FAQ
What features does syncfusion-react-tabs cover?
The syncfusion-react-tabs skill covers Syncfusion React Tab setup, header positions, overflow modes, content rendering strategies, drag-and-drop reorder, animations, ARIA accessibility, and state persistence. Agents follow SKILL.md patterns and API references.
When should developers use syncfusion-react-tabs?
Developers should use syncfusion-react-tabs when building React UIs that switch related content via tabs—settings panels, dashboards, or wizards—using Syncfusion instead of custom or unlicensed tab libraries.