The Component Dev Kit (CDK) is a set of behavior primitives for building components. To use the drag and drop directives, first install @angular/cdk from npm. You can do this from your terminal using Angular CLI:
Add the cdkDropList directive to a parent element to group draggable elements into a reorderable collection. This defines where draggable elements can be dropped. The draggable elements in the drop list group rearrange automatically as an element moves.
The drag and drop directives don't update your data model. To update the data model, listen to the cdkDropListDropped event (once the user finishes dragging) and update the data model manually.
import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop sorting */@Component({ selector: 'cdk-drag-drop-sorting-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDropList, CdkDrag],})export class CdkDragDropSortingExample { movies = [ 'Episode I - The Phantom Menace', 'Episode II - Attack of the Clones', 'Episode III - Revenge of the Sith', 'Episode IV - A New Hope', 'Episode V - The Empire Strikes Back', 'Episode VI - Return of the Jedi', 'Episode VII - The Force Awakens', 'Episode VIII - The Last Jedi', 'Episode IX – The Rise of Skywalker', ]; drop(event:CdkDragDrop<string[]>) {moveItemInArray(this.movies, event.previousIndex, event.currentIndex); }}
The cdkDropList directive supports transferring draggable elements between connected drop lists. There are two ways to connect one or more cdkDropList instances together:
Set the cdkDropListConnectedTo property to another drop list.
Wrap the elements in an element with the cdkDropListGroup attribute.
The cdkDropListConnectedTo directive works both with a direct reference to another cdkDropList or by referencing the id of another drop container.
<!-- This is valid --><div cdkDropList #listOne="cdkDropList" [cdkDropListConnectedTo]="[listTwo]"></div><div cdkDropList #listTwo="cdkDropList" [cdkDropListConnectedTo]="[listOne]"></div><!-- This is valid as well --><div cdkDropList id="list-one" [cdkDropListConnectedTo]="['list-two']"></div><div cdkDropList id="list-two" [cdkDropListConnectedTo]="['list-one']"></div>
Use the cdkDropListGroup directive if you have an unknown number of connected drop lists to set up the connection automatically. Any new cdkDropList that is added under a group automatically connects to all other lists.
<div cdkDropListGroup> <!-- All lists in here will be connected. --> @for (list of lists; track list) { <div cdkDropList></div> }</div>
By default, a user can move cdkDrag elements from one container into another connected container. For more fine-grained control over which elements can be dropped into a container, use cdkDropListEnterPredicate. Angular calls the predicate whenever a draggable element enters a new container. Depending on whether the predicate returns true or false, the item may or may not be allowed into the new container.
import {CdkDrag,CdkDragDrop,CdkDropList,moveItemInArray,transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop enter predicate */@Component({ selector: 'cdk-drag-drop-enter-predicate-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDropList, CdkDrag],})export class CdkDragDropEnterPredicateExample { all = [1, 2, 3, 4, 5, 6, 7, 8, 9]; even = [10]; drop(event:CdkDragDrop<number[]>) { 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, ); } } /** Predicate function that only allows even numbers to be dropped into a list. */ evenPredicate(item:CdkDrag<number>) { return item.data % 2 === 0; } /** Predicate function that doesn't allow items to be dropped into a list. */ noReturnPredicate() { return false; }}
You can associate some arbitrary data with both cdkDrag and cdkDropList by setting cdkDragData or cdkDropListData, respectively. You can bind to the events fired from both directives that will include this data, allowing you to easily identify the origin of the drag or drop interaction.
By default, the user can drag the entire cdkDrag element to move it around. To restrict the user to only be able to do so using a handle element, add the cdkDragHandle directive to an element inside of cdkDrag. You can have as many cdkDragHandle elements as you want.
app.component.html
<div class="example-box" cdkDrag> I can only be dragged using the handle <div class="example-handle" cdkDragHandle> <svg width="24px" fill="currentColor" viewBox="0 0 24 24"> <path d="M10 9h4V6h3l-5-5-5 5h3v3zm-1 1H6V7l-5 5 5 5v-3h3v-4zm14 2l-5-5v3h-3v4h3v3l5-5zm-9 3h-4v3H7l5 5 5-5h-3v-3z"></path> <path d="M0 0h24v24H0z" fill="none"></path> </svg> </div></div>
app.component.ts
import {CdkDrag, CdkDragHandle} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop with a handle */@Component({ selector: 'cdk-drag-drop-handle-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag, CdkDragHandle],})export class CdkDragDropHandleExample {}
A preview element becomes visible when a cdkDrag element is being dragged. By default, the preview is a clone of the original element positioned next to the user's cursor.
To customize the preview, provide a custom template via *cdkDragPreview. The custom preview won't match the size of the original dragged element since assumptions aren't made about the element's content. To match the size of the element for the drag preview, pass true to the matchSize input.
The cloned element removes its id attribute in order to avoid having multiple elements with the same id on the page. This will cause any CSS that targets that id not to be applied.
By default, Angular inserts the cdkDrag preview into the <body> of the page in order to avoid issues with positioning and overflow. This may not be desirable in some cases because the preview won't have its inherited styles applied.
You can change where Angular inserts the preview using the cdkDragPreviewContainer input on cdkDrag. The possible values are:
Value
Description
Advantages
Disadvantages
global
Default value. Angular inserts the preview into the or the closest shadow root.
Preview won't be affected by z-index or overflow: hidden. It also won't affect :nth-child selectors and flex layouts.
Doesn't retain inherited styles.
parent
Angular inserts the preview inside the parent of the element that is being dragged.
Preview inherits the same styles as the dragged element.
Preview may be clipped by overflow: hidden or be placed under other elements due to z-index. Furthermore, it can affect :nth-child selectors and some flex layouts.
Angular inserts the preview into the specified element.
Preview inherits styles from the specified container element.
Preview may be clipped by overflow: hidden or be placed under other elements due to z-index. Furthermore, it can affect :nth-child selectors and some flex layouts.
While a cdkDrag element is being dragged, the directive creates a placeholder element that shows where the element will be placed when dropped. By default, the placeholder is a clone of the element that is being dragged. You can replace the placeholder with a custom one using the *cdkDragPlaceholder directive:
import {CdkDrag,CdkDragDrop,CdkDragPlaceholder,CdkDropList,moveItemInArray,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop custom placeholder */@Component({ selector: 'cdk-drag-drop-custom-placeholder-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDropList, CdkDrag, CdkDragPlaceholder],})export class CdkDragDropCustomPlaceholderExample { movies = [ 'Episode I - The Phantom Menace', 'Episode II - Attack of the Clones', 'Episode III - Revenge of the Sith', 'Episode IV - A New Hope', 'Episode V - The Empire Strikes Back', 'Episode VI - Return of the Jedi', 'Episode VII - The Force Awakens', 'Episode VIII - The Last Jedi', 'Episode IX - The Rise of Skywalker', ]; drop(event:CdkDragDrop<string[]>) {moveItemInArray(this.movies, event.previousIndex, event.currentIndex); }}
Set the cdkDragRootElement attribute if there's an element that you want to make draggable but you don't have direct access to it.
The attribute accepts a selector and looks up the DOM until it finds an element that matches the selector. If an element is found, it becomes draggable. This is useful for cases such as making a dialog draggable.
app.component.html
<button type="button" (click)="openDialog()">Open a draggable dialog</button><ng-template> <div class="example-dialog-content" cdkDrag cdkDragRootElement=".cdk-overlay-pane"> Drag the dialog around! </div></ng-template>
app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Overlay, OverlayRef} from '@angular/cdk/overlay';import {TemplatePortal} from '@angular/cdk/portal';import {AfterViewInit,Component,OnDestroy,TemplateRef,ViewChild,ViewContainerRef, inject,} from '@angular/core';/** * @title Drag&Drop with alternate root element */@Component({ selector: 'cdk-drag-drop-root-element-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag],})export class CdkDragDropRootElementExample implementsAfterViewInit, OnDestroy { private _overlay = inject(Overlay); private _viewContainerRef = inject(ViewContainerRef); @ViewChild(TemplateRef) _dialogTemplate!:TemplateRef<any>; private _overlayRef!: OverlayRef; private _portal!: TemplatePortal; ngAfterViewInit() { this._portal = new TemplatePortal(this._dialogTemplate, this._viewContainerRef); this._overlayRef = this._overlay.create({ positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(), hasBackdrop: true, }); this._overlayRef.backdropClick().subscribe(() => this._overlayRef.detach()); } ngOnDestroy() { this._overlayRef.dispose(); } openDialog() { this._overlayRef.attach(this._portal); }}
By default, cdkDrag elements not in a cdkDropList move from their normal DOM position only when a user manually moves the element. Use the cdkDragFreeDragPosition input to explicitly set the element’s position. A common use case for this is restoring a draggable element's position after a user has navigated away and then returned.
app.component.html
<p> <button type="button" (click)="changePosition()">Change element position</button></p><div class="example-box" cdkDrag [cdkDragFreeDragPosition]="dragPosition"> Drag me around</div>
app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Programmatically setting the free drag position */@Component({ selector: 'cdk-drag-drop-free-drag-position-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag],})export class CdkDragDropFreeDragPositionExample { dragPosition = {x: 0, y: 0}; changePosition() { this.dragPosition = {x: this.dragPosition.x + 50, y: this.dragPosition.y + 50}; }}
To stop the user from being able to drag a cdkDrag element outside of another element, pass a CSS selector to the cdkDragBoundary attribute. This attribute accepts a selector and looks up the DOM until it finds an element that matches it. If a match is found, the element becomes the boundary that the draggable element can't be dragged outside of cdkDragBoundary can also be used when cdkDrag is placed inside a cdkDropList.
app.component.html
<div class="example-boundary"> <div class="example-box" cdkDragBoundary=".example-boundary" cdkDrag> I can only be dragged within the dotted container </div></div>
app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop boundary */@Component({ selector: 'cdk-drag-drop-boundary-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag],})export class CdkDragDropBoundaryExample {}
By default, cdkDrag allows free movement in all directions. To restrict dragging to a specific axis, set cdkDragLockAxis to either "x" or "y"on cdkDrag. To restrict dragging for multiple draggable elements within cdkDropList, set cdkDropListLockAxis on cdkDropList instead.
app.component.html
<div class="example-box" cdkDragLockAxis="y" cdkDrag> I can only be dragged up/down</div><div class="example-box" cdkDragLockAxis="x" cdkDrag> I can only be dragged left/right</div>
app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop position locking */@Component({ selector: 'cdk-drag-drop-axis-lock-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag],})export class CdkDragDropAxisLockExample {}
By default when the user puts their pointer down on a cdkDrag, the dragging sequence starts. This behavior might not be desirable in cases like fullscreen draggable elements on touch devices where the user might accidentally trigger a drag event as they scroll on the page.
You can delay the dragging sequence using the cdkDragStartDelay input. The input waits for the user to hold down their pointer for the specified number of milliseconds before dragging the element.
app.component.html
<div class="example-box" cdkDrag [cdkDragStartDelay]="1000"> Dragging starts after one second</div>
app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Delay dragging */@Component({ selector: 'cdk-drag-drop-delay-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', imports: [CdkDrag],})export class CdkDragDropDelayExample {}
If you want to disable dragging for a particular drag item, set the cdkDragDisabled input on a cdkDrag item to true or false. You can disable an entire list using the cdkDropListDisabled input on a cdkDropList. It is also possible to disable a specific handle via cdkDragHandleDisabled on cdkDragHandle.
By default, the cdkDropList sorts the draggable elements by moving them around using a CSS transform. This allows for the sorting to be animated which provides a better user experience. However this also comes with the drawback that the drop list works only in one direction: vertically or horizontally.
If you have a sortable list that needs to wrap onto new lines, you can set cdkDropListOrientation attribute to mixed. This causes the list to use a different strategy of sorting the elements which involves moving them in the DOM. However the list can no longer animate the sorting action .
By default, cdkDrag elements are sorted into any position inside of a cdkDropList. To change this behavior, set the cdkDropListSortPredicate attribute which takes in a function. The predicate function is called whenever a draggable element is about to be moved into a new index within the drop list. If the predicate returns true, the item will be moved into the new index, otherwise it will keep its current position.
There are cases where draggable elements can be dragged out of one cdkDropList into another, however the user shouldn't be able to sort them within the source list. For these cases, add the cdkDropListSortingDisabled attribute to prevent the draggable elements in a cdkDropList from sorting. This preserves the dragged element's initial position in the source list if it does not get dragged to a new valid position.
By default, when an item is dragged from one list to another, it is moved out of its original list. However, you can configure the directives to copy the item, leaving the original item in the source list.
To enable copying, you can set the cdkDropListHasAnchor input. This tells the cdkDropList to create an "anchor" element that stays in the original container and doesn't move with the item. If the user moves the item back into the original container, the anchor is removed automatically. The anchor element can be styled by targeting the .cdk-drag-anchor CSS class.
Combining cdkDropListHasAnchor with cdkDropListSortingDisabled makes it possible to construct a list from which a user can copy items without being able to reorder the source list (e.g. a product list and a shopping cart).
Both cdkDrag and cdkDropList directives only apply essential styles needed for functionality. Applications can customize their styles by targeting these specified CSS classes.
CSS class name
Description
.cdk-drop-list
Selector for the cdkDropList container elements.
.cdk-drag
Selector for cdkDrag elements.
.cdk-drag-disabled
Selector for disabled cdkDrag elements.
.cdk-drag-handle
Selector for the host element of the cdkDragHandle.
.cdk-drag-preview
Selector for the drag preview element. This is the element that appears next to the cursor as a user drags an element in a sortable list.
The element looks exactly like the element that is being dragged unless customized with a custom template through *cdkDragPreview.
.cdk-drag-placeholder
Selector for the drag placeholder element. This is the element that is shown in the spot where the draggable element will be dragged to once the dragging action ends.
This element looks exactly like the element that is being sorted unless customized with the cdkDragPlaceholder directive.
.cdk-drop-list-dragging
Selector for cdkDropList container element that has a draggable element currently being dragged.
.cdk-drop-list-disabled
Selector for cdkDropList container elements that are disabled.
.cdk-drop-list-receiving
Selector for cdkDropList container element that has a draggable element it can receive from a connected drop list that is currently being dragged.
.cdk-drag-anchor
Selector for the anchor element that is created when cdkDropListHasAnchor is enabled. This element indicates the position from which the dragged item started.
If your draggable items are inside a scrollable container (e.g., a div with overflow: auto), automatic scrolling will not work unless the scrollable container has the cdkScrollable directive. Without it, the CDK cannot detect or control the scroll behavior of the container during drag operations.
The CDK's drag-and-drop functionality can be integrated with different components. Common use cases include sortable MatTable components and sortable MatTabGroup components.