New to Kendo UI for Angular? Start a free 30-day trial
Scrollable Kendo Menu with Arrow Navigation
Environment
Product | Progress® Kendo UI for Angular Menu |
Description
By default, the Kendo UI for Angular Menu does not include built-in horizontal scrolling or navigation arrows for overflowing items. This article demonstrates how to implement a horizontally scrollable Menu with left and right arrow buttons for navigation, similar to the scrolling behavior in the Kendo UI for Angular TabStrip component.
This Knowledge Base article also answers the following questions:
- How do I add horizontal scrolling to the Kendo UI for Angular Menu?
- How can I display navigation arrows when the Menu items overflow?
- What is the recommended way to implement a scrollable Menu similar to the TabStrip in Kendo UI for Angular?
Solution
To enable horizontal scrolling with arrow navigation:
-
Import the required dependencies:
typescriptimport { Component, ElementRef, ViewChild } from '@angular/core'; import { KENDO_BUTTONS } from '@progress/kendo-angular-buttons'; import { KENDO_ICONS } from '@progress/kendo-angular-icons'; import { KENDO_MENUS } from '@progress/kendo-angular-menu'; import { chevronLeftIcon, chevronRightIcon, SVGIcon, } from '@progress/kendo-svg-icons';
-
Wrap the Menu in a scrollable container:
html<div class="menu-container"> <div class="menu-scroll-wrapper"> <button class="kendo-arrow-btn" kendoButton fillMode="flat" (click)="scrollLeft()" [disabled]="atStart" aria-label="Scroll left" > <kendo-svg-icon [icon]="arrowChevronLeftIcon"></kendo-svg-icon> </button> <div #scrollContainer class="menu-scroll-container"> <kendo-menu [items]="items"></kendo-menu> </div> <button class="kendo-arrow-btn" kendoButton fillMode="flat" (click)="scrollRight()" [disabled]="atEnd" aria-label="Scroll right" > <kendo-svg-icon [icon]="arrowChevronRightIcon"></kendo-svg-icon> </button> </div> </div>
-
Implement the component class with scroll functionality:
typescript@Component({ standalone: true, selector: 'my-app', imports: [KENDO_MENUS, KENDO_ICONS, KENDO_BUTTONS], template: `...`, // Template shown in step 2 styles: [` .menu-container { max-width: 800px; margin: 0 auto; padding: 0 16px; } .menu-scroll-wrapper { display: flex; align-items: center; width: 100%; background: var(--kendo-base-bg, #fff); border-radius: var(--kendo-border-radius, 4px); box-shadow: var(--kendo-box-shadow-depth-1, 0 1px 4px rgba(0, 0, 0, 0.07)); padding: 0 8px; } .kendo-arrow-btn { min-width: 32px; min-height: 32px; border-radius: 50%; border: none; background: var(--kendo-button-bg, #f5f5f5); color: var(--kendo-button-text, #656565); display: flex; align-items: center; justify-content: center; margin: 0 4px; box-shadow: none; transition: background 0.2s; } .kendo-arrow-btn:disabled { background: var(--kendo-disabled-bg, #f0f0f0); color: var(--kendo-disabled-text, #bdbdbd); cursor: not-allowed; opacity: 0.7; } .menu-scroll-container { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; /* Firefox */ flex: 1 1 auto; min-width: 0; scroll-behavior: smooth; padding: 4px 0; } .menu-scroll-container::-webkit-scrollbar { display: none; /* Safari and Chrome */ } `], }) export class AppComponent { public items: MenuItem[] = items; public arrowChevronLeftIcon: SVGIcon = chevronLeftIcon; public arrowChevronRightIcon: SVGIcon = chevronRightIcon; @ViewChild('scrollContainer', { static: true }) scrollContainer!: ElementRef<HTMLDivElement>; public atStart = true; public atEnd = false; public ngAfterViewInit(): void { this.updateArrows(); this.scrollContainer.nativeElement.addEventListener('scroll', () => this.updateArrows() ); } public scrollLeft(): void { this.scrollContainer.nativeElement.scrollBy({ left: -150, behavior: 'smooth', }); } public scrollRight(): void { this.scrollContainer.nativeElement.scrollBy({ left: 150, behavior: 'smooth', }); } public updateArrows(): void { const el = this.scrollContainer.nativeElement; this.atStart = el.scrollLeft === 0; this.atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 1; } }
-
Define the interfaces and menu items:
typescriptexport interface MenuItem { text: string; items?: MenuItem[]; } export const items: MenuItem[] = [ { text: 'File', items: [ { text: 'New' }, { text: 'Open' }, { text: 'Recent', items: [ { text: 'Project1' }, { text: 'Project2' }, { text: 'Project3' }, { text: 'Project4' }, ], }, { text: 'Save' }, { text: 'Save As' }, { text: 'Close' }, ], }, // ...other menu items... ];
How It Works
- The Menu is placed inside a horizontally scrollable container.
- Arrow buttons on each side call
scrollLeft()
andscrollRight()
to scroll the Menu. - The buttons are automatically disabled when the scroll position is at the start or end.
- The
updateArrows()
method keeps the button states in sync with the scroll position.
Tip: You can further style the wrapper and buttons to match your application's look and feel.
Demo
Change Theme
Theme
Loading ...