Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to drag and drop in Angular with CDK

Implementing drag and drop functionality in Angular applications requires handling complex mouse and touch events, managing state, and providing visual feedback. With over 10 years of experience building Angular applications and as the creator of CoreUI, I’ve implemented drag and drop features in numerous enterprise dashboards and admin panels. From my expertise, the most efficient approach is to use the Angular CDK (Component Dev Kit) drag-drop module, which provides a robust, accessible, and well-tested solution. This method handles all the complexity for you while remaining highly customizable.

Use Angular CDK’s cdkDrag and cdkDropList directives to enable drag and drop with minimal code.

import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'

@Component({
  selector: 'app-task-list',
  template: `
    <div cdkDropList (cdkDropListDropped)="drop($event)">
      <div *ngFor="let task of tasks" cdkDrag>
        {{ task }}
      </div>
    </div>
  `
})
export class TaskListComponent {
  tasks = ['Task 1', 'Task 2', 'Task 3']

  drop(event: CdkDragDrop<string[]>) {
    moveItemInArray(this.tasks, event.previousIndex, event.currentIndex)
  }
}

How It Works

The cdkDropList directive creates a drop zone, while cdkDrag makes individual elements draggable. When an item is dropped, the cdkDropListDropped event fires with information about the drag operation. The moveItemInArray utility function reorders the array based on the previous and current index, updating your data model automatically.

Installing Angular CDK

First, install the Angular CDK package:

npm install @angular/cdk

Then import the drag-drop module in your component or module:

import { DragDropModule } from '@angular/cdk/drag-drop'

@NgModule({
  imports: [DragDropModule],
  declarations: [TaskListComponent]
})
export class AppModule {}

The DragDropModule provides all the directives and utilities needed for drag and drop functionality. Once imported, you can use the cdkDrag and cdkDropList directives in your templates.

Moving Items Between Lists

Transfer items between multiple drop lists by connecting them:

import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'

@Component({
  selector: 'app-kanban',
  template: `
    <div class="board">
      <div cdkDropList #todoList="cdkDropList"
           [cdkDropListData]="todo"
           [cdkDropListConnectedTo]="[doneList]"
           (cdkDropListDropped)="drop($event)">
        <div *ngFor="let item of todo" cdkDrag>{{ item }}</div>
      </div>

      <div cdkDropList #doneList="cdkDropList"
           [cdkDropListData]="done"
           [cdkDropListConnectedTo]="[todoList]"
           (cdkDropListDropped)="drop($event)">
        <div *ngFor="let item of done" cdkDrag>{{ item }}</div>
      </div>
    </div>
  `
})
export class KanbanComponent {
  todo = ['Design', 'Development']
  done = ['Planning']

  drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex)
    } else {
      transferArrayItem(
        event.previousContainer.data,
        event.container.data,
        event.previousIndex,
        event.currentIndex
      )
    }
  }
}

The cdkDropListConnectedTo property links multiple drop lists together. When dropping, check if the item stayed in the same container using moveItemInArray, or moved to a different container using transferArrayItem. The cdkDropListData binds each list to its data array.

Customizing Drag Preview

Control the appearance of the drag preview with custom templates:

@Component({
  template: `
    <div cdkDropList>
      <div *ngFor="let item of items" cdkDrag>
        {{ item.name }}
        <div *cdkDragPreview class="preview">
          <strong>{{ item.name }}</strong>
        </div>
        <div *cdkDragPlaceholder class="drop-area">
          Drop here
        </div>
      </div>
    </div>
  `,
  styles: [`
    .preview {
      background: #2196f3;
      color: white;
      padding: 8px;
      border-radius: 4px;
    }
    .drop-area {
      background: #ddd;
      border: 2px dashed #999;
      min-height: 60px;
    }
  `]
})
export class CustomDragComponent {
  items = [
    { name: 'Item 1' },
    { name: 'Item 2' }
  ]
}

The *cdkDragPreview directive defines what the user sees while dragging, and *cdkDragPlaceholder shows where the item will be dropped. These templates give you full control over the visual feedback during drag operations.

Restricting Drag Axis

Limit dragging to horizontal or vertical movement:

@Component({
  template: `
    <div cdkDropList cdkDropListOrientation="horizontal">
      <div *ngFor="let item of items" cdkDrag cdkDragLockAxis="x">
        {{ item }}
      </div>
    </div>
  `
})
export class HorizontalListComponent {
  items = ['A', 'B', 'C']
}

The cdkDragLockAxis property restricts movement to either the x (horizontal) or y (vertical) axis. Setting cdkDropListOrientation="horizontal" tells the drop list to arrange items horizontally and handle reordering accordingly.

Drag Handle

Create a drag handle so items can only be dragged by a specific element:

@Component({
  template: `
    <div cdkDropList>
      <div *ngFor="let item of items" cdkDrag>
        <div cdkDragHandle class="handle">⋮⋮</div>
        <div class="content">{{ item }}</div>
      </div>
    </div>
  `,
  styles: [`
    .handle {
      cursor: move;
      padding: 8px;
    }
  `]
})
export class HandleDragComponent {
  items = ['Task 1', 'Task 2', 'Task 3']
}

The cdkDragHandle directive marks an element as the drag handle. Users can only initiate dragging by clicking on this element, preventing accidental drags when interacting with other parts of the item.

Disabling Drag

Conditionally disable dragging for specific items:

@Component({
  template: `
    <div cdkDropList>
      <div *ngFor="let item of items"
           cdkDrag
           [cdkDragDisabled]="item.locked">
        {{ item.name }}
        <span *ngIf="item.locked">🔒</span>
      </div>
    </div>
  `
})
export class ConditionalDragComponent {
  items = [
    { name: 'Editable', locked: false },
    { name: 'Locked', locked: true }
  ]
}

The cdkDragDisabled property accepts a boolean to enable or disable dragging for individual items. This is useful for implementing locked items, read-only modes, or permission-based dragging.

Best Practice Note

This is the same approach we use in CoreUI Angular components to implement reorderable lists, kanban boards, and sortable tables. The Angular CDK provides excellent accessibility support out of the box, including keyboard navigation with arrow keys and screen reader announcements. For production applications, always test drag and drop functionality with keyboard-only navigation to ensure accessibility. If you’re setting up a new Angular project, check our guide on how to create a new Angular project for best practices. Consider using CoreUI’s Angular components which integrate seamlessly with CDK drag-drop for a complete UI solution.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author