New to Kendo UI for AngularStart a free 30-day trial

Reordering TabStrip Tabs with Drag and Drop

Updated on Jan 20, 2026

Environment

ProductProgress® Kendo UI for Angular TabStrip

Description

How can I enable drag and drop functionality to reorder tabs in the Kendo Angular TabStrip? I want users to move tabs within the TabStrip by dragging them with a handle icon.

This Knowledge Base article also answers the following questions:

  • How to implement tab reordering in the Kendo UI for Angular TabStrip?
  • Is it possible to drag and drop tabs in the Kendo UI for Angular TabStrip?
  • How can I use the Kendo Drag and Drop directives with the Kendo UI for Angular TabStrip?

Solution

The Kendo UI for Angular TabStrip does not have built-in support for tab reordering via drag and drop. However, you can achieve this functionality by integrating the Kendo Drag and Drop directives with the TabStrip component.

The following example demonstrates tab reordering using the Drag and Drop utility package.

Change Theme
Theme
Loading ...

To implement tab reordering, follow these steps:

  1. Wrap the TabStrip in a container element and apply the kendoDragTargetContainer and kendoDropTargetContainer directives.

    html
    <div
      #wrapper
      kendoDragTargetContainer
      kendoDropTargetContainer
      dragTargetFilter=".k-tabstrip-item"
      dropTargetFilter=".k-tabstrip-item"
      [dragData]="dragData"
      [hint]="{ hintClass: 'tabHint' }"
      (onDrop)="onDrop($event)"
      (onDragOver)="onDragOver($event)"
      (onDragLeave)="clearDropIndicators()"
      dragHandle=".reorder-icon"
    >
      <kendo-tabstrip>
        <!-- tabs -->
      </kendo-tabstrip>
    </div>
  2. Add a custom drag handle icon to each tab by implementing a custom template using the kendoTabTitle directive. Include a reorder icon from the SVG icons package that will serve as the drag handle.

    html
    @for (tab of tabs; track $index) {
      <kendo-tabstrip-tab [title]="tab.title" [selected]="tab.selected">
        <ng-template kendoTabTitle>
          <kendo-svgicon class="reorder-icon" [icon]="reorderSVG"></kendo-svgicon>
          <span class="tab-title">{{ tab.title }}</span>
        </ng-template>
        <ng-template kendoTabContent>
          <p>{{ tab.content }}</p>
        </ng-template>
      </kendo-tabstrip-tab>
    }
  3. Track tab positions by adding a custom data attribute to each tab element using the AfterViewInit lifecycle hook. These attributes help identify the source and destination tabs during drag and drop operations.

    typescript
    private readonly TAB_INDEX_ATTR = 'data-kendo-tab-index';
    
    @ViewChild('wrapper', { read: ElementRef })
    wrapper!: ElementRef;
    
    ngAfterViewInit(): void {
      this.updateTabIndices();
    }
    
    private updateTabIndices(): void {
      setTimeout(() => {
        const tabItems = this.wrapper.nativeElement.querySelectorAll('.k-tabstrip-item');
        tabItems.forEach((item: HTMLElement, index: number) => {
          item.setAttribute(this.TAB_INDEX_ATTR, index.toString());
        });
      });
    }
  4. Define a dragData function to provide custom data for the events of the DropTargetContainerDirective. In this function, retrieve the data-kendo-tab-index attribute from the drag target and return an object with the source tab index.

    typescript
    public dragData = ({ dragTarget }: { dragTarget: HTMLElement }) => {
      if (dragTarget instanceof HTMLLIElement) {
        const index = dragTarget.getAttribute(this.TAB_INDEX_ATTR);
        if (index !== null) {
          return { fromIndex: +index };
        }
      }
      return null;
    };
  5. Provide visual feedback during drag operations by handling the onDragOver event. In this example, we add CSS classes to indicate whether the tab will be dropped before or after the target tab based on the cursor position.

    typescript
    private readonly DROP_INDICATOR_BEFORE = 'drop-indicator-before';
    private readonly DROP_INDICATOR_AFTER = 'drop-indicator-after';
    
    public onDragOver(e: DropTargetEvent): void {
      if (!e.dragData || !(e.dropTarget instanceof HTMLLIElement)) {
        return;
      }
    
      this.clearDropIndicators();
    
      const isDropAfter = this.isDropAfterMiddle(e.dropTarget, e.dragEvent.clientX);
      e.dropTarget.classList.add(
        isDropAfter ? this.DROP_INDICATOR_AFTER : this.DROP_INDICATOR_BEFORE
      );
    }
    
    private isDropAfterMiddle(element: HTMLElement, clientX: number): boolean {
      const rect = element.getBoundingClientRect();
      const middle = rect.left + rect.width / 2;
      return clientX >= middle;
    }
  6. Handle the onDrop event to perform the actual tab reordering. Calculate the correct destination index based on the drag direction and cursor position.

    typescript
    public onDrop(e: DropTargetEvent): void {
      this.clearDropIndicators();
    
      if (!e.dragData || !(e.dragTarget instanceof HTMLLIElement) ||
          !(e.dropTarget instanceof HTMLLIElement)) {
        return;
      }
    
      const { fromIndex } = e.dragData;
      const toIndexAttr = e.dropTarget.getAttribute(this.TAB_INDEX_ATTR);
    
      if (toIndexAttr === null) {
        return;
      }
    
      const toIndex = +toIndexAttr;
      const destinationIndex = this.calculateDestinationIndex(
        fromIndex,
        toIndex,
        e.dropTarget,
        e.dragEvent.clientX
      );
    
      if (destinationIndex === null) {
        return;
      }
    
      this.reorderTabs(fromIndex, destinationIndex);
    }
  7. Update the tabs array by removing the dragged tab from its original position and inserting it at the calculated destination index. Then, update the DOM indices of the tabs and notify the Drag and Drop directives to synchronize their internal state with the updated DOM.

    typescript
    @ViewChild('wrapper', { read: DragTargetContainerDirective })
    dragTargetContainer!: DragTargetContainerDirective;
    
    @ViewChild('wrapper', { read: DropTargetContainerDirective })
    dropTargetContainer!: DropTargetContainerDirective;
    
    private reorderTabs(fromIndex: number, destinationIndex: number): void {
      const [movedTab] = this.tabs.splice(fromIndex, 1);
      this.tabs.splice(destinationIndex, 0, movedTab);
      this.tabs = [...this.tabs];
    
      this.updateTabIndices();
      this.dragTargetContainer.notify();
      this.dropTargetContainer.notify();
    }

See Also

In this article
EnvironmentDescriptionSolutionSee Also
Not finding the help you need?
Contact Support