Overview
This page describes the drag and drop directives which lets you quickly create drag and drop interfaces with the following:
- Free dragging
- Create a list of reorderable draggable elements
- Transfer draggable elements between lists
- Dragging animations
- Lock draggable elements along an axis or element
- Add custom drag handles
- Add previews on drag
- Add custom drag placeholder
For the full API reference, please see the Angular CDK's drag and drop API reference page.
Before you start
CDK Installation
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:
ng add @angular/cdk
Importing drag and drop
To use drag and drop, import what you need from the directives in your component.
import {Component} from '@angular/core';import {CdkDrag} from '@angular/cdk/drag-drop';@Component({ selector: 'my-custom-component', templateUrl: 'my-custom-component.html', standalone: true, imports: [CdkDrag],})export class DragDropExample {}
Create draggable elements
You can make any element draggable by adding the cdkDrag
directive. By default, all draggable elements support free dragging.
app/app.component.html
<div class="example-box" cdkDrag> Drag me around</div>
app/app.component.ts
import {CdkDrag} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Basic Drag&Drop */@Component({ selector: 'cdk-drag-drop-overview-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDrag],})export class CdkDragDropOverviewExample {}
app/app.component.css
.example-box { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; position: relative; z-index: 1; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}
Create a list of reorderable draggable elements
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.
app/app.component.html
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> @for (movie of movies; track movie) { <div class="example-box" cdkDrag>{{movie}}</div> }</div>
app/app.component.ts
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', standalone: true, 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); }}
app/app.component.css
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { border: none; box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
You can use the CDK_DROP_LIST
injection token that can be used to reference instances of cdkDropList
. For more information see the dependency injection guide and the drop list injection token API.
Transfer draggable elements between lists
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>
app/app.component.html
<div class="example-container"> <h2>To do</h2> <div cdkDropList #todoList="cdkDropList" [cdkDropListData]="todo" [cdkDropListConnectedTo]="[doneList]" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of todo; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div></div><div class="example-container"> <h2>Done</h2> <div cdkDropList #doneList="cdkDropList" [cdkDropListData]="done" [cdkDropListConnectedTo]="[todoList]" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of done; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div></div>
app/app.component.ts
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray, transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop connected sorting */@Component({ selector: 'cdk-drag-drop-connected-sorting-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag],})export class CdkDragDropConnectedSortingExample { todo = ['Get to work', 'Pick up groceries', 'Go home', 'Fall asleep']; done = ['Get up', 'Brush teeth', 'Take a shower', 'Check e-mail', 'Walk dog']; 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, ); } }}
app/app.component.css
.example-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top;}.example-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
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>
app/app.component.html
<div cdkDropListGroup> <div class="example-container"> <h2>To do</h2> <div cdkDropList [cdkDropListData]="todo" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of todo; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div> </div> <div class="example-container"> <h2>Done</h2> <div cdkDropList [cdkDropListData]="done" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of done; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div> </div></div>
app/app.component.ts
import { CdkDrag, CdkDragDrop, CdkDropList, CdkDropListGroup, moveItemInArray, transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop connected sorting group */@Component({ selector: 'cdk-drag-drop-connected-sorting-group-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropListGroup, CdkDropList, CdkDrag],})export class CdkDragDropConnectedSortingGroupExample { todo = ['Get to work', 'Pick up groceries', 'Go home', 'Fall asleep']; done = ['Get up', 'Brush teeth', 'Take a shower', 'Check e-mail', 'Walk dog']; 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, ); } }}
app/app.component.css
.example-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top;}.example-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
You can use the CDK_DROP_LIST_GROUP
injection token that can be used to reference instances of cdkDropListGroup
. For more information see the dependency injection guide and the drop list group injection token API.
Selective dragging
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.
app/app.component.html
<div class="example-container"> <h2>Available numbers</h2> <div id="all" cdkDropList [cdkDropListData]="all" cdkDropListConnectedTo="even" class="example-list" (cdkDropListDropped)="drop($event)" [cdkDropListEnterPredicate]="noReturnPredicate"> @for (number of all; track number) { <div class="example-box" [cdkDragData]="number" cdkDrag>{{number}}</div> } </div></div><div class="example-container"> <h2>Even numbers</h2> <div id="even" cdkDropList [cdkDropListData]="even" cdkDropListConnectedTo="all" class="example-list" (cdkDropListDropped)="drop($event)" [cdkDropListEnterPredicate]="evenPredicate"> @for (number of even; track number) { <div class="example-box" cdkDrag [cdkDragData]="number">{{number}}</div> } </div></div>
app/app.component.ts
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', standalone: true, 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; }}
app/app.component.css
.example-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top;}.example-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Attach data
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.
@for (list of lists; track list) { <div cdkDropList [cdkDropListData]="list" (cdkDropListDropped)="drop($event)"> @for (item of list; track item) { <div cdkDrag [cdkDragData]="item"></div> } </div>}
Dragging customizations
Customize drag handle
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/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/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', standalone: true, imports: [CdkDrag, CdkDragHandle],})export class CdkDragDropHandleExample {}
app/app.component.css
.example-box { width: 200px; height: 200px; padding: 10px; box-sizing: border-box; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; position: relative; z-index: 1; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.example-handle { position: absolute; top: 10px; right: 10px; color: #ccc; cursor: move; width: 24px; height: 24px;}
You can use the CDK_DRAG_HANDLE
injection token that can be used to reference instances of cdkDragHandle
. For more information see the dependency injection guide and the drag handle injection token API.
Customize drag preview
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.
app/app.component.html
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> @for (movie of movies; track movie) { <div class="example-box" cdkDrag> {{movie.title}} <img *cdkDragPreview [src]="movie.poster" [alt]="movie.title"> </div> }</div>
app/app.component.ts
import { CdkDrag, CdkDragDrop, CdkDragPreview, CdkDropList, moveItemInArray,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop custom preview */@Component({ selector: 'cdk-drag-drop-custom-preview-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag, CdkDragPreview],})export class CdkDragDropCustomPreviewExample { // tslint:disable:max-line-length movies = [ { title: 'Episode I - The Phantom Menace', poster: 'https://upload.wikimedia.org/wikipedia/en/4/40/Star_Wars_Phantom_Menace_poster.jpg', }, { title: 'Episode II - Attack of the Clones', poster: 'https://upload.wikimedia.org/wikipedia/en/3/32/Star_Wars_-_Episode_II_Attack_of_the_Clones_%28movie_poster%29.jpg', }, { title: 'Episode III - Revenge of the Sith', poster: 'https://upload.wikimedia.org/wikipedia/en/9/93/Star_Wars_Episode_III_Revenge_of_the_Sith_poster.jpg', }, { title: 'Episode IV - A New Hope', poster: 'https://upload.wikimedia.org/wikipedia/en/8/87/StarWarsMoviePoster1977.jpg', }, { title: 'Episode V - The Empire Strikes Back', poster: 'https://upload.wikimedia.org/wikipedia/en/3/3f/The_Empire_Strikes_Back_%281980_film%29.jpg', }, { title: 'Episode VI - Return of the Jedi', poster: 'https://upload.wikimedia.org/wikipedia/en/b/b2/ReturnOfTheJediPoster1983.jpg', }, { title: 'Episode VII - The Force Awakens', poster: 'https://upload.wikimedia.org/wikipedia/en/a/a2/Star_Wars_The_Force_Awakens_Theatrical_Poster.jpg', }, { title: 'Episode VIII - The Last Jedi', poster: 'https://upload.wikimedia.org/wikipedia/en/7/7f/Star_Wars_The_Last_Jedi.jpg', }, { title: 'Episode IX – The Rise of Skywalker', poster: 'https://upload.wikimedia.org/wikipedia/en/a/af/Star_Wars_The_Rise_of_Skywalker_poster.jpg', }, ]; // tslint:enable:max-line-length drop(event: CdkDragDrop<{title: string; poster: string}[]>) { moveItemInArray(this.movies, event.previousIndex, event.currentIndex); }}
app/app.component.css
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
You can use the CDK_DRAG_PREVIEW
injection token that can be used to reference instances of cdkDragPreview
. For more information see the dependency injection guide and the drag preview injection token API.
Customize drag insertion point
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. |
ElementRef or HTMLElement |
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. |
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update previewContainer
within the config if the value is global
or parent
. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Customize drag placeholder
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:
app/app.component.html
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> @for (movie of movies; track movie) { <div class="example-box" cdkDrag> <div class="example-custom-placeholder" *cdkDragPlaceholder></div> {{movie}} </div> }</div>
app/app.component.ts
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', standalone: true, 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); }}
app/app.component.css
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-custom-placeholder { background: #ccc; border: dotted 3px #999; min-height: 60px; transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
You can use the CDK_DRAG_PLACEHOLDER
injection token that can be used to reference instances of cdkDragPlaceholder
. For more information see the dependency injection guide and the drag placeholder injection token API.
Customize drag root element
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/app.component.html
<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/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', standalone: true, imports: [CdkDrag],})export class CdkDragDropRootElementExample implements AfterViewInit, 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); }}
app/app.component.css
.example-dialog-content { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: flex; justify-content: center; align-items: center; background: #fff; border-radius: 4px; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-dialog-content:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update rootElementSelector
within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Set DOM position of a draggable element
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/app.component.html
<p> <button (click)="changePosition()">Change element position</button></p><div class="example-box" cdkDrag [cdkDragFreeDragPosition]="dragPosition"> Drag me around</div>
app/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', standalone: true, imports: [CdkDrag],})export class CdkDragDropFreeDragPositionExample { dragPosition = {x: 0, y: 0}; changePosition() { this.dragPosition = {x: this.dragPosition.x + 50, y: this.dragPosition.y + 50}; }}
app/app.component.css
.example-box { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; position: relative; z-index: 1; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}
Restrict movement within an element
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/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/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', standalone: true, imports: [CdkDrag],})export class CdkDragDropBoundaryExample {}
app/app.component.css
.example-box { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: inline-flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; margin-right: 25px; position: relative; z-index: 1; box-sizing: border-box; padding: 10px; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.example-boundary { width: 400px; height: 400px; max-width: 100%; border: dotted #ccc 2px;}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update boundaryElement within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Restrict movement along an axis
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/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/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', standalone: true, imports: [CdkDrag],})export class CdkDragDropAxisLockExample {}
app/app.component.css
.example-box { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: inline-flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; margin-right: 25px; position: relative; z-index: 1; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update lockAxis
within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Delay dragging
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/app.component.html
<div class="example-box" cdkDrag [cdkDragStartDelay]="1000"> Dragging starts after one second</div>
app/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', standalone: true, imports: [CdkDrag],})export class CdkDragDropDelayExample {}
app/app.component.css
.example-box { width: 200px; height: 200px; border: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); cursor: move; display: flex; justify-content: center; align-items: center; text-align: center; background: #fff; border-radius: 4px; position: relative; z-index: 1; transition: box-shadow 200ms cubic-bezier(0, 0, 0.2, 1); box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);}.example-box:active { box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update dragStartDelay within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Disable dragging
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
.
app/app.component.html
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of items; track item) { <div class="example-box" cdkDrag [cdkDragDisabled]="item.disabled">{{item.value}}</div> }</div>
app/app.component.ts
import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop disabled */@Component({ selector: 'cdk-drag-drop-disabled-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag],})export class CdkDragDropDisabledExample { items = [ {value: 'I can be dragged', disabled: false}, {value: 'I cannot be dragged', disabled: true}, {value: 'I can also be dragged', disabled: false}, ]; drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.items, event.previousIndex, event.currentIndex); }}
app/app.component.css
.example-list { width: 500px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: block; background: white; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.example-box.cdk-drag-disabled { background: #ccc; cursor: not-allowed; user-select: none;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update draggingDisabled
within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Sorting customizations
List orientation
By default, the cdkDropList
directive assumes lists are vertical. This can be changed by setting the cdkDropListOrientation
property to horizontal.
app/app.component.html
<div cdkDropList cdkDropListOrientation="horizontal" class="example-list" (cdkDropListDropped)="drop($event)"> @for (timePeriod of timePeriods; track timePeriod) { <div class="example-box" cdkDrag>{{timePeriod}}</div> }</div>
app/app.component.ts
import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop horizontal sorting */@Component({ selector: 'cdk-drag-drop-horizontal-sorting-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag],})export class CdkDragDropHorizontalSortingExample { timePeriods = [ 'Bronze age', 'Iron age', 'Middle ages', 'Early modern period', 'Long nineteenth century', ]; drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.timePeriods, event.previousIndex, event.currentIndex); }}
app/app.component.css
.example-list { width: 1000px; max-width: 100%; border: solid 1px #ccc; min-height: 60px; display: flex; flex-direction: row; background: white; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border-right: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px; flex-grow: 1; flex-basis: 0;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update listOrientation
within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
List wrapping
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 .
app/app.component.html
<div cdkDropList cdkDropListOrientation="mixed" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of items; track item) { <div class="example-box" cdkDrag>{{item}}</div> }</div>
app/app.component.ts
import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop horizontal wrapping list */@Component({ selector: 'cdk-drag-drop-mixed-sorting-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag],})export class CdkDragDropMixedSortingExample { items = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine']; drop(event: CdkDragDrop<string[]>) { moveItemInArray(this.items, event.previousIndex, event.currentIndex); }}
app/app.component.css
.example-list { display: flex; flex-wrap: wrap; width: 505px; max-width: 100%; gap: 15px; padding: 15px; border: solid 1px #ccc; min-height: 60px; border-radius: 4px; overflow: hidden;}.example-box { padding: 20px 10px; border: solid 1px #ccc; border-radius: 4px; color: rgba(0, 0, 0, 0.87); display: inline-block; box-sizing: border-box; cursor: move; background: white; text-align: center; font-size: 14px; min-width: 115px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Selective sorting
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.
app/app.component.html
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)" [cdkDropListSortPredicate]="sortPredicate"> @for (number of numbers; track number) { <div class="example-box" [cdkDragData]="number" cdkDrag>{{number}}</div> }</div>
app/app.component.ts
import {CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop sort predicate */@Component({ selector: 'cdk-drag-drop-sort-predicate-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropList, CdkDrag],})export class CdkDragDropSortPredicateExample { numbers = [1, 2, 3, 4, 5, 6, 7, 8]; drop(event: CdkDragDrop<unknown>) { moveItemInArray(this.numbers, event.previousIndex, event.currentIndex); } /** * Predicate function that only allows even numbers to be * sorted into even indices and odd numbers at odd indices. */ sortPredicate(index: number, item: CdkDrag<number>) { return (index + 1) % 2 === item.data % 2; }}
app/app.component.css
.example-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block; width: 400px; max-width: 100%;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Disable sorting
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.
app/app.component.html
<div cdkDropListGroup> <div class="example-container"> <h2>Available items</h2> <div cdkDropList [cdkDropListData]="items" class="example-list" cdkDropListSortingDisabled (cdkDropListDropped)="drop($event)"> @for (item of items; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div> </div> <div class="example-container"> <h2>Shopping basket</h2> <div cdkDropList [cdkDropListData]="basket" class="example-list" (cdkDropListDropped)="drop($event)"> @for (item of basket; track item) { <div class="example-box" cdkDrag>{{item}}</div> } </div> </div></div>
app/app.component.ts
import { CdkDrag, CdkDragDrop, CdkDropList, CdkDropListGroup, moveItemInArray, transferArrayItem,} from '@angular/cdk/drag-drop';import {Component} from '@angular/core';/** * @title Drag&Drop disabled sorting */@Component({ selector: 'cdk-drag-drop-disabled-sorting-example', templateUrl: 'app.component.html', styleUrl: 'app.component.css', standalone: true, imports: [CdkDropListGroup, CdkDropList, CdkDrag],})export class CdkDragDropDisabledSortingExample { items = ['Carrots', 'Tomatoes', 'Onions', 'Apples', 'Avocados']; basket = ['Oranges', 'Bananas', 'Cucumbers']; 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, ); } }}
app/app.component.css
HTMLTSCSS.example-container { width: 400px; max-width: 100%; margin: 0 25px 25px 0; display: inline-block; vertical-align: top;}.example-list { border: solid 1px #ccc; min-height: 60px; background: white; border-radius: 4px; overflow: hidden; display: block;}.example-box { padding: 20px 10px; border-bottom: solid 1px #ccc; color: rgba(0, 0, 0, 0.87); display: flex; flex-direction: row; align-items: center; justify-content: space-between; box-sizing: border-box; cursor: move; background: white; font-size: 14px;}.cdk-drag-preview { box-sizing: border-box; border-radius: 4px; box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);}.cdk-drag-placeholder { opacity: 0;}.cdk-drag-animating { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}.example-box:last-child { border: none;}.example-list.cdk-drop-list-dragging .example-box:not(.cdk-drag-placeholder) { transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);}
Alternatively, you can modify the CDK_DRAG_CONFIG
injection token to update sortingDisabled within the config. For more information see the dependency injection guide, drag config injection token API, and the drag drop config API.
Customize animations
Drag and drop supports animations for both:
- Sorting an draggable element inside a list
- Moving the draggable element from the position that the user dropped it to the final position inside the list
To set up your animations, define a CSS transition that targets the transform property. The following classes can be used for animations:
CSS class name | Result of adding transition |
---|---|
.cdk-drag | Animate draggable elements as they are being sorted. |
.cdk-drag-animating | Animate the draggable element from its dropped position to the final position within the cdkDropList .This CSS class is applied to a cdkDrag element only when the dragging action has stopped. |
Styling
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. |