DevExtreme v24.2 is now available.

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

Your search did not match any results.

React Menu - Scrolling

The DevExtreme Menu component supports submenu item scrolling. If combined item height exceeds screen size or a pre-defined height limit, a scrollbar appears on-screen.

You can use one of the following event handlers to configure submenus as requirements dictate:

These handlers can access the root submenu element (submenuContainer) and data from root and submenu items (itemData).

In this demo, the onSubmenuShowing function limits submenu height to 200px.

Backend API
import React, { useCallback, useState } from 'react'; import Menu, { MenuTypes } from 'devextreme-react/menu'; import CheckBox, { CheckBoxTypes } from 'devextreme-react/check-box'; import notify from 'devextreme/ui/notify'; import service from './data.ts'; const SUBMENU_HEIGHT = 200; const products = service.getProducts(); const App = () => { const [limitSubmenuHeight, setLimitSubmenuHeight] = useState(false); const itemClick = useCallback((e: MenuTypes.ItemClickEvent) => { if (!e.itemData.items) { notify(`The "${e.itemData.text}" item was clicked`, 'success', 1500); } }, []); const limitSubmenuHeightOnMouseClick = useCallback((e: CheckBoxTypes.ValueChangedEvent) => { setLimitSubmenuHeight(e.value); }, [setLimitSubmenuHeight]); const onSubmenuShowing = useCallback(({ submenuContainer }: HTMLElement) => { submenuContainer.style.maxHeight = limitSubmenuHeight ? `${SUBMENU_HEIGHT}px` : ''; }, [limitSubmenuHeight]); return ( <div className="demo-container"> <div className="content"> <div className="label">Catalog:</div> <Menu dataSource={products} onItemClick={itemClick} onSubmenuShowing={onSubmenuShowing} /> </div> <div className="options"> <div className="caption">Options</div> <div className="option"> <CheckBox text={`Limit submenu height to ${SUBMENU_HEIGHT}px`} value={limitSubmenuHeight} onValueChanged={limitSubmenuHeightOnMouseClick} /> </div> </div> </div> ); }; export default App;
import React, { useCallback, useState } from 'react'; import Menu from 'devextreme-react/menu'; import CheckBox from 'devextreme-react/check-box'; import notify from 'devextreme/ui/notify'; import service from './data.js'; const SUBMENU_HEIGHT = 200; const products = service.getProducts(); const App = () => { const [limitSubmenuHeight, setLimitSubmenuHeight] = useState(false); const itemClick = useCallback((e) => { if (!e.itemData.items) { notify(`The "${e.itemData.text}" item was clicked`, 'success', 1500); } }, []); const limitSubmenuHeightOnMouseClick = useCallback( (e) => { setLimitSubmenuHeight(e.value); }, [setLimitSubmenuHeight], ); const onSubmenuShowing = useCallback( ({ submenuContainer }) => { submenuContainer.style.maxHeight = limitSubmenuHeight ? `${SUBMENU_HEIGHT}px` : ''; }, [limitSubmenuHeight], ); return ( <div className="demo-container"> <div className="content"> <div className="label">Catalog:</div> <Menu dataSource={products} onItemClick={itemClick} onSubmenuShowing={onSubmenuShowing} /> </div> <div className="options"> <div className="caption">Options</div> <div className="option"> <CheckBox text={`Limit submenu height to ${SUBMENU_HEIGHT}px`} value={limitSubmenuHeight} onValueChanged={limitSubmenuHeightOnMouseClick} /> </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'), );
const products = [ { text: 'Electronics', items: [ { text: 'Computers', items: [ { text: 'Desktops', }, { text: 'Laptops', }, { text: 'Tablets', }, { text: 'Monitors', }, { text: 'Printers', }, { text: 'Scanners', }, { text: 'Servers', }, { text: 'Network Devices', }, { text: 'Warranties', }, { text: 'Projectors & Screens' }, { text: 'Computer Accessories', }, ] }, { text: 'Cell Phones', items: [ { text: 'Home & Office Phones', }, { text: 'Smart Phones & PDAs', }, { text: 'Senior Cell Phones' }, { text: 'Mobile Broadband Devices', }, { text: 'Cell Phone Accessories', }, ] }, { text: 'Camera & Photo', items: [ { text: 'Action Cameras', }, { text: 'Camcorders', }, { text: 'Digital Cameras', }, { text: 'Digital Picture Frames', }, { text: 'Film Cameras', }, { text: 'Professional Video Cameras', }, { text: 'Flashes', }, { text: 'Lenses', }, { text: 'Photo Printers', }, { text: 'Film & Slide Scanners', }, { text: 'Slide Projectors', }, { text: 'Slide Viewers', }, { text: 'Tripods & Monopods', }, { text: 'Underwater Video & Photography', }, { text: 'Camera Accessories', }, ] }, { text: 'Audio', items: [ { text: 'Boomboxes', }, { text: 'DVD & CD Player Bags', }, { text: 'Hi-Fi & Home Audio Accessories', }, { text: 'Media Streaming Devices', }, { text: 'MP3 & Digital Media Players', }, { text: 'MP3 Player Accessories', }, { text: 'Personal Cassette Players', }, { text: 'Personal CD Players', }, { text: 'Compact Stereos', }, { text: 'Speakers', }, ] }, { text: 'Home Appliances', items: [ { text: 'Air Conditioners', }, { text: 'Coffee Machines', }, { text: 'Fans', }, { text: 'Lamps', }, { text: 'Microwaves', }, { text: 'Refrigerators', }, { text: 'Washers & Dryers' }, { text: ' Water Heaters', }, { 'text': 'Vacuum' }, { text: 'Steaming & Ironing' } ] }, ] }, { text: 'Entertainment', items: [ { text: 'Films & Music', items: [ { text: 'TV Shows', }, { text: 'DVD', }, { text: 'Blu-ray', }, { text: 'CD', }, { text: 'SACD', }, { text: 'Vinyl', }, { text: 'Cassette' }, { text: 'Musical Instruments', }, { text: 'DJ Equipment' } ] }, { text: 'Games & Toys', items: [ { text: 'PC Games', }, { text: 'Virtual Reality', }, { text: 'Boxed Games', }, { text: 'Download Games', }, { text: 'Handheld Game Systems' }, { text: 'Legacy Systems', }, { text: 'Cards', }, ] }, ] }, { text: 'Healthcare & Living', items: [ { text: 'Sports & Outdoors', items: [ { text: 'Camping', }, { text: 'Fitness', }, { text: 'Football', }, { text: 'Cycling', }, { text: 'Running', }, { text: 'Sport Electronics', }, { text: 'Sport Apparel' }, { text: 'Trainers & Tennis Shoes', }, ] }, { text: 'Beauty', items: [ { text: 'Bath & Shower', }, { text: 'Skin Care', }, { text: 'Hair Care', }, { text: 'Nail Care', }, { text: 'Make-up' }, { text: 'Fragrance', }, { text: 'Salon & SPA Equipment', }, { text: 'Tools & Accessories', }, ] }, { text: 'Fashion', items: [ { text: 'Clothing', }, { text: 'Jewellery', }, { text: 'Shoes', }, { text: 'Handbags & Shoulder Bags', }, { text: 'Watches', }, { text: 'Accessories', } ] } ] }, ]; export interface ProductItemType { text: string; } export interface ProductType { text: string; items: (ProductItemType | ProductType)[]; } export default { getProducts(): ProductType[] { return products; }, };
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, }, 'openai': { 'esModule': true, }, }, paths: { 'npm:': 'https://unpkg.com/', 'bundles:': '../../../../bundles/', 'externals:': '../../../../bundles/externals/', }, defaultExtension: 'js', map: { 'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js', 'typescript': 'npm:typescript@4.2.4/lib/typescript.js', 'jszip': 'npm:jszip@3.10.1/dist/jszip.min.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/prop-types.js', 'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js', 'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign', 'devextreme': 'npm:devextreme@link:../../packages/devextreme/artifacts/npm/devextreme/cjs', 'devextreme-react': 'npm:devextreme-react@link:../../packages/devextreme-react/npm/cjs', 'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/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/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.8/standalone.js', 'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js', }, packages: { 'devextreme': { defaultExtension: 'js', }, 'devextreme-react': { main: 'index.js', }, 'devextreme/events/utils': { main: 'index', }, 'devextreme/localization/messages': { format: 'json', defaultExtension: 'json', }, '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'));
const products = [ { text: 'Electronics', items: [ { text: 'Computers', items: [ { text: 'Desktops', }, { text: 'Laptops', }, { text: 'Tablets', }, { text: 'Monitors', }, { text: 'Printers', }, { text: 'Scanners', }, { text: 'Servers', }, { text: 'Network Devices', }, { text: 'Warranties', }, { text: 'Projectors & Screens' }, { text: 'Computer Accessories', }, ] }, { text: 'Cell Phones', items: [ { text: 'Home & Office Phones', }, { text: 'Smart Phones & PDAs', }, { text: 'Senior Cell Phones' }, { text: 'Mobile Broadband Devices', }, { text: 'Cell Phone Accessories', }, ] }, { text: 'Camera & Photo', items: [ { text: 'Action Cameras', }, { text: 'Camcorders', }, { text: 'Digital Cameras', }, { text: 'Digital Picture Frames', }, { text: 'Film Cameras', }, { text: 'Professional Video Cameras', }, { text: 'Flashes', }, { text: 'Lenses', }, { text: 'Photo Printers', }, { text: 'Film & Slide Scanners', }, { text: 'Slide Projectors', }, { text: 'Slide Viewers', }, { text: 'Tripods & Monopods', }, { text: 'Underwater Video & Photography', }, { text: 'Camera Accessories', }, ] }, { text: 'Audio', items: [ { text: 'Boomboxes', }, { text: 'DVD & CD Player Bags', }, { text: 'Hi-Fi & Home Audio Accessories', }, { text: 'Media Streaming Devices', }, { text: 'MP3 & Digital Media Players', }, { text: 'MP3 Player Accessories', }, { text: 'Personal Cassette Players', }, { text: 'Personal CD Players', }, { text: 'Compact Stereos', }, { text: 'Speakers', }, ] }, { text: 'Home Appliances', items: [ { text: 'Air Conditioners', }, { text: 'Coffee Machines', }, { text: 'Fans', }, { text: 'Lamps', }, { text: 'Microwaves', }, { text: 'Refrigerators', }, { text: 'Washers & Dryers' }, { text: ' Water Heaters', }, { 'text': 'Vacuum' }, { text: 'Steaming & Ironing' } ] }, ] }, { text: 'Entertainment', items: [ { text: 'Films & Music', items: [ { text: 'TV Shows', }, { text: 'DVD', }, { text: 'Blu-ray', }, { text: 'CD', }, { text: 'SACD', }, { text: 'Vinyl', }, { text: 'Cassette' }, { text: 'Musical Instruments', }, { text: 'DJ Equipment' } ] }, { text: 'Games & Toys', items: [ { text: 'PC Games', }, { text: 'Virtual Reality', }, { text: 'Boxed Games', }, { text: 'Download Games', }, { text: 'Handheld Game Systems' }, { text: 'Legacy Systems', }, { text: 'Cards', }, ] }, ] }, { text: 'Healthcare & Living', items: [ { text: 'Sports & Outdoors', items: [ { text: 'Camping', }, { text: 'Fitness', }, { text: 'Football', }, { text: 'Cycling', }, { text: 'Running', }, { text: 'Sport Electronics', }, { text: 'Sport Apparel' }, { text: 'Trainers & Tennis Shoes', }, ] }, { text: 'Beauty', items: [ { text: 'Bath & Shower', }, { text: 'Skin Care', }, { text: 'Hair Care', }, { text: 'Nail Care', }, { text: 'Make-up' }, { text: 'Fragrance', }, { text: 'Salon & SPA Equipment', }, { text: 'Tools & Accessories', }, ] }, { text: 'Fashion', items: [ { text: 'Clothing', }, { text: 'Jewellery', }, { text: 'Shoes', }, { text: 'Handbags & Shoulder Bags', }, { text: 'Watches', }, { text: 'Accessories', } ] } ] }, ]; export default { getProducts() { return products; }, };
<!DOCTYPE html> <html lang="en"> <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=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/24.2.3/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 id="app"></div> </body> </html>
.demo-container { display: flex; } .label { font-size: 22px; padding-bottom: 24px; } .content { flex-grow: 1; width: 100%; min-width: 500px; overflow: clip; } .options { display: flex; flex-direction: column; padding: 20px; background-color: rgba(191, 191, 191, 0.15); } .caption { font-size: 18px; font-weight: 500; } .option { margin-top: 10px; }