DevExtreme v23.2 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Overview

The Tabs component allows you to create a tabbed UI to navigate between pages or views. You can create tab items in the items array, or populate tab items from a dataSource.

Customize Tab Contents and Appearance

You can initialize a tab’s contents (text, icons and badges) with values from underlying data objects. This example demonstrates this technique.

Use the drop-down editors on the right to change the component's orientation, styling mode, and icon position.

You can also specify an item template (itemTemplate) to customize tabs.

Configure Overflow Behavior

The Tabs component stretches to fit its container if you do not specify the component's width. When the total tab width exceeds the component’s width, navigation buttons appear. A user can click these buttons to scroll through the tabs. Use the showNavButtons and scrollByContent properties to control this behavior.

Backend API
import React, { useCallback, useState } from 'react'; import Tabs from 'devextreme-react/tabs'; import SelectBox from 'devextreme-react/select-box'; import CheckBox from 'devextreme-react/check-box'; import { orientations, tabsText, stylingModeLabel, iconPositionLabel, tabsIconAndText, stylingModes, iconPositions, tabsIcon, orientationLabel, } from './data.ts'; const STRICT_WIDTH_CLASS = 'strict-width'; function OptionWrapper(props) { return ( <div className="option"> {props.caption && <span>{props.caption}</span>} {props.children} </div> ); } const App = () => { const [orientation, setOrientation] = useState(orientations[0]); const [stylingMode, setStylingMode] = useState(stylingModes[1]); const [iconPosition, setIconPosition] = useState(iconPositions[0]); const [showNavigation, setShowNavigation] = useState(false); const [scrollContent, setScrollContent] = useState(false); const [fullWidth, setFullWidth] = useState(false); const [width, setWidth] = useState('auto'); const [rtlEnabled, setRtlEnabled] = useState(false); const [widgetWrapperClasses, setWidgetWrapperClasses] = useState('widget-wrapper widget-wrapper-horizontal'); const enforceWidthConstraint = useCallback( (shouldRestrictWidth) => { const callback = (prevClasses: string) => { const restClasses = prevClasses.split(' ').filter((className) => className !== STRICT_WIDTH_CLASS).join(' '); const strictWidthClass = shouldRestrictWidth ? STRICT_WIDTH_CLASS : ''; return `${restClasses} ${strictWidthClass}`; }; setWidgetWrapperClasses(callback); }, [setWidgetWrapperClasses], ); const stylingModeChanged = useCallback((e) => { setStylingMode(e.value); }, [setStylingMode]); const iconPositionChanged = useCallback((e) => { setIconPosition(e.value); }, [setIconPosition]); const orientationChanged = useCallback((e) => { const isVertical = e.value === 'vertical'; const callback = (prevClasses: string) => { const restClasses = prevClasses .split(' ') .filter((className) => (className !== (isVertical ? 'widget-wrapper-horizontal' : 'widget-wrapper-vertical'))) .join(' '); return `${restClasses} widget-wrapper-${e.value}`; }; setWidgetWrapperClasses(callback); setOrientation(e.value); }, [setOrientation, setWidgetWrapperClasses]); const showNavigationChanged = useCallback((e) => { const shouldRestrictWidth = e.value || scrollContent; enforceWidthConstraint(shouldRestrictWidth); setShowNavigation(e.value); }, [scrollContent, setShowNavigation, enforceWidthConstraint]); const scrollContentChanged = useCallback((e) => { const shouldRestrictWidth = e.value || showNavigation; enforceWidthConstraint(shouldRestrictWidth); setScrollContent(e.value); }, [showNavigation, setScrollContent, enforceWidthConstraint]); const fullWidthChanged = useCallback((e) => { setFullWidth(e.value); setWidth(e.value ? '100%' : 'auto'); }, [setFullWidth, setWidth]); const rtlEnabledChanged = useCallback((e) => { setRtlEnabled(e.value); }, [setRtlEnabled]); return ( <div className="tabs-demo"> <div className="widget-container"> <div className={widgetWrapperClasses}> <Tabs id="withText" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsText} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> <Tabs id="withIconAndText" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsIconAndText} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> <Tabs id="withIcon" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsIcon} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> </div> </div> <div className="options"> <div className="caption">Options</div> <OptionWrapper caption='Orientation'> <SelectBox items={orientations} inputAttr={orientationLabel} value={orientation} onValueChanged={orientationChanged} /> </OptionWrapper> <OptionWrapper caption='Styling mode'> <SelectBox items={stylingModes} inputAttr={stylingModeLabel} value={stylingMode} onValueChanged={stylingModeChanged} /> </OptionWrapper> <OptionWrapper caption='Icon position'> <SelectBox items={iconPositions} inputAttr={iconPositionLabel} value={iconPosition} onValueChanged={iconPositionChanged} /> </OptionWrapper> <div className="option"> <CheckBox id="show-navigation-buttons" text="Show navigation buttons" value={showNavigation} onValueChanged={showNavigationChanged} /> </div> <div className="option"> <CheckBox text="Scroll content" value={scrollContent} onValueChanged={scrollContentChanged} /> </div> <div className="option"> <CheckBox text="Full width" value={fullWidth} onValueChanged={fullWidthChanged} /> </div> <div className="option"> <CheckBox text="Right-to-left mode" value={rtlEnabled} onValueChanged={rtlEnabledChanged} /> </div> </div> </div> ); }; export default App;
import React, { useCallback, useState } from 'react'; import Tabs from 'devextreme-react/tabs'; import SelectBox from 'devextreme-react/select-box'; import CheckBox from 'devextreme-react/check-box'; import { orientations, tabsText, stylingModeLabel, iconPositionLabel, tabsIconAndText, stylingModes, iconPositions, tabsIcon, orientationLabel, } from './data.js'; const STRICT_WIDTH_CLASS = 'strict-width'; function OptionWrapper(props) { return ( <div className="option"> {props.caption && <span>{props.caption}</span>} {props.children} </div> ); } const App = () => { const [orientation, setOrientation] = useState(orientations[0]); const [stylingMode, setStylingMode] = useState(stylingModes[1]); const [iconPosition, setIconPosition] = useState(iconPositions[0]); const [showNavigation, setShowNavigation] = useState(false); const [scrollContent, setScrollContent] = useState(false); const [fullWidth, setFullWidth] = useState(false); const [width, setWidth] = useState('auto'); const [rtlEnabled, setRtlEnabled] = useState(false); const [widgetWrapperClasses, setWidgetWrapperClasses] = useState( 'widget-wrapper widget-wrapper-horizontal', ); const enforceWidthConstraint = useCallback( (shouldRestrictWidth) => { const callback = (prevClasses) => { const restClasses = prevClasses .split(' ') .filter((className) => className !== STRICT_WIDTH_CLASS) .join(' '); const strictWidthClass = shouldRestrictWidth ? STRICT_WIDTH_CLASS : ''; return `${restClasses} ${strictWidthClass}`; }; setWidgetWrapperClasses(callback); }, [setWidgetWrapperClasses], ); const stylingModeChanged = useCallback( (e) => { setStylingMode(e.value); }, [setStylingMode], ); const iconPositionChanged = useCallback( (e) => { setIconPosition(e.value); }, [setIconPosition], ); const orientationChanged = useCallback( (e) => { const isVertical = e.value === 'vertical'; const callback = (prevClasses) => { const restClasses = prevClasses .split(' ') .filter( (className) => className !== (isVertical ? 'widget-wrapper-horizontal' : 'widget-wrapper-vertical'), ) .join(' '); return `${restClasses} widget-wrapper-${e.value}`; }; setWidgetWrapperClasses(callback); setOrientation(e.value); }, [setOrientation, setWidgetWrapperClasses], ); const showNavigationChanged = useCallback( (e) => { const shouldRestrictWidth = e.value || scrollContent; enforceWidthConstraint(shouldRestrictWidth); setShowNavigation(e.value); }, [scrollContent, setShowNavigation, enforceWidthConstraint], ); const scrollContentChanged = useCallback( (e) => { const shouldRestrictWidth = e.value || showNavigation; enforceWidthConstraint(shouldRestrictWidth); setScrollContent(e.value); }, [showNavigation, setScrollContent, enforceWidthConstraint], ); const fullWidthChanged = useCallback( (e) => { setFullWidth(e.value); setWidth(e.value ? '100%' : 'auto'); }, [setFullWidth, setWidth], ); const rtlEnabledChanged = useCallback( (e) => { setRtlEnabled(e.value); }, [setRtlEnabled], ); return ( <div className="tabs-demo"> <div className="widget-container"> <div className={widgetWrapperClasses}> <Tabs id="withText" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsText} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> <Tabs id="withIconAndText" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsIconAndText} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> <Tabs id="withIcon" width={width} defaultSelectedIndex={0} rtlEnabled={rtlEnabled} dataSource={tabsIcon} scrollByContent={scrollContent} showNavButtons={showNavigation} orientation={orientation} stylingMode={stylingMode} iconPosition={iconPosition} /> </div> </div> <div className="options"> <div className="caption">Options</div> <OptionWrapper caption="Orientation"> <SelectBox items={orientations} inputAttr={orientationLabel} value={orientation} onValueChanged={orientationChanged} /> </OptionWrapper> <OptionWrapper caption="Styling mode"> <SelectBox items={stylingModes} inputAttr={stylingModeLabel} value={stylingMode} onValueChanged={stylingModeChanged} /> </OptionWrapper> <OptionWrapper caption="Icon position"> <SelectBox items={iconPositions} inputAttr={iconPositionLabel} value={iconPosition} onValueChanged={iconPositionChanged} /> </OptionWrapper> <div className="option"> <CheckBox id="show-navigation-buttons" text="Show navigation buttons" value={showNavigation} onValueChanged={showNavigationChanged} /> </div> <div className="option"> <CheckBox text="Scroll content" value={scrollContent} onValueChanged={scrollContentChanged} /> </div> <div className="option"> <CheckBox text="Full width" value={fullWidth} onValueChanged={fullWidthChanged} /> </div> <div className="option"> <CheckBox text="Right-to-left mode" value={rtlEnabled} onValueChanged={rtlEnabledChanged} /> </div> </div> </div> ); }; export default App;
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.tsx'; ReactDOM.render( <App />, document.getElementById('app'), );
import { Orientation, TabsIconPosition, TabsStyle } from 'devextreme/common'; export const tabsText = [ { id: 0, text: 'User', }, { id: 1, text: 'Analytics', }, { id: 2, text: 'Clients', }, { id: 3, text: 'Orders', }, { id: 4, text: 'Favorites', }, { id: 5, text: 'Search', }, ]; export const tabsIconAndText = [ { id: 0, text: 'User', icon: 'user', }, { id: 1, text: 'Analytics', icon: 'chart', }, { id: 2, text: 'Clients', icon: 'accountbox', }, { id: 3, text: 'Orders', icon: 'ordersbox', }, { id: 4, text: 'Favorites', icon: 'bookmark', }, { id: 5, text: 'Search', icon: 'search', }, ]; export const tabsIcon = [ { id: 0, icon: 'user', }, { id: 1, icon: 'chart', }, { id: 2, icon: 'accountbox', }, { id: 3, icon: 'ordersbox', }, { id: 4, icon: 'bookmark', }, { id: 5, icon: 'search', }, ]; export const orientations: Orientation[] = ['horizontal', 'vertical']; export const stylingModes: TabsStyle[] = [ 'primary', 'secondary', ]; export const orientationLabel = { 'aria-label': 'Orientation' }; export const stylingModeLabel = { 'aria-label': 'Styling Mode' }; export const iconPositionLabel = { 'aria-label': 'Icon Position' }; export const iconPositions: TabsIconPosition[] = [ 'top', 'start', 'end', 'bottom', ];
window.exports = window.exports || {}; window.config = { transpiler: 'ts', typescriptOptions: { module: 'system', emitDecoratorMetadata: true, experimentalDecorators: true, jsx: 'react', }, meta: { 'react': { 'esModule': true, }, 'typescript': { 'exports': 'ts', }, 'devextreme/time_zone_utils.js': { 'esModule': true, }, 'devextreme/localization.js': { 'esModule': true, }, 'devextreme/viz/palette.js': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@4.2.4/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'react': 'npm:react@17.0.2/umd/react.development.js', 'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js', 'prop-types': 'npm:prop-types@15.8.1/prop-types.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@23.2.5/cjs', 'devextreme-react': 'npm:devextreme-react@23.2.5/cjs', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@1.6.4/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.51/dist/dx-gantt.js', '@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12', 'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js', 'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js', 'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js', 'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js', 'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js', 'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js', 'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js', 'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js', 'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3', // SystemJS plugins 'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js', 'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js', // Prettier 'prettier/standalone': 'npm:prettier@2.8.4/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: '', }, 'devextreme/events': { main: 'index', }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js', }, }, packageConfigPaths: [ 'npm:@devextreme/*/package.json', 'npm:@devextreme/runtime@3.0.12/inferno/package.json', ], babelOptions: { sourceMaps: false, stage0: true, react: true, }, }; System.config(window.config);
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.js'; ReactDOM.render(<App />, document.getElementById('app'));
export const tabsText = [ { id: 0, text: 'User', }, { id: 1, text: 'Analytics', }, { id: 2, text: 'Clients', }, { id: 3, text: 'Orders', }, { id: 4, text: 'Favorites', }, { id: 5, text: 'Search', }, ]; export const tabsIconAndText = [ { id: 0, text: 'User', icon: 'user', }, { id: 1, text: 'Analytics', icon: 'chart', }, { id: 2, text: 'Clients', icon: 'accountbox', }, { id: 3, text: 'Orders', icon: 'ordersbox', }, { id: 4, text: 'Favorites', icon: 'bookmark', }, { id: 5, text: 'Search', icon: 'search', }, ]; export const tabsIcon = [ { id: 0, icon: 'user', }, { id: 1, icon: 'chart', }, { id: 2, icon: 'accountbox', }, { id: 3, icon: 'ordersbox', }, { id: 4, icon: 'bookmark', }, { id: 5, icon: 'search', }, ]; export const orientations = ['horizontal', 'vertical']; export const stylingModes = ['primary', 'secondary']; export const orientationLabel = { 'aria-label': 'Orientation' }; export const stylingModeLabel = { 'aria-label': 'Styling Mode' }; export const iconPositionLabel = { 'aria-label': 'Icon Position' }; export const iconPositions = ['top', 'start', 'end', 'bottom'];
<!DOCTYPE html> <html> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.2.5/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import("./index.tsx"); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"></div> </div> </body> </html>
.tabs-demo { display: flex; } .strict-width { max-width: 340px; } .dx-theme-generic .strict-width { max-width: 250px; } .widget-container { display: flex; align-items: center; justify-content: center; flex-grow: 1; width: 100%; min-width: 200px; padding: 16px 32px; overflow: clip; } .widget-wrapper { display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 80px; width: 100%; } .widget-wrapper-vertical { width: 100%; flex-direction: row; align-items: center; } .options { display: flex; flex-direction: column; flex-shrink: 0; padding: 20px; background-color: rgba(191, 191, 191, 0.15); } .caption { font-weight: 500; font-size: 18px; } #show-navigation-buttons { margin-top: 22px; } .option { margin-top: 20px; } .dx-tabs { max-width: 100%; } .dx-tabs-vertical { height: 216px; } .dx-viewport:not(.dx-theme-generic) .dx-tabs-horizontal { border-block-end: 1px solid rgb(225, 225, 225, 0.4); } .dx-viewport:not(.dx-theme-generic) .dx-tabs-vertical { height: 232px; border-inline-end: 1px solid rgb(225, 225, 225, 0.4); }