> For AI agents: the complete documentation index is available at https://gultyayev.github.io/ngx-virtual-dnd/llms.txt, the full documentation bundle is available at https://gultyayev.github.io/ngx-virtual-dnd/llms-full.txt.

# ngx-virtual-dnd

Drag and drop for virtual scrolling

> Sortable lists and boards for Angular that stay fast with thousands of items. Only the visible rows are rendered, and drag and drop still works across all of them.

[Get started](/guide/start/introduction) | [Live demo](https://gultyayev.github.io/ngx-virtual-dnd/demo/)

## Features

- **Built for virtual lists**: Hit-testing and drop positions are computed from geometry, not from sibling DOM nodes, so drops land correctly on rows that are not rendered.
- **Dynamic item heights**: Rows are measured with ResizeObserver and cached by ID. Lookups use prefix sums and binary search.
- **Multiple lists**: Move items between any lists in the same group, with edge auto-scroll while dragging.
- **Keyboard and touch**: Space to pick up, arrow keys to move, Escape to cancel. Touch drags support a hold delay so lists still scroll.
- **Signals first**: Standalone components and directives for Angular 21 and 22. Drop events plus small helpers update your signal-based state.
- **Guides for AI assistants**: An installable agent skill and llms.txt give coding assistants the same guidance as these docs.

Quick look


## A sortable list of 1,000 rows in one template
Only the rows on screen are in the DOM. Drops arrive as events, and a helper applies them to your signal.
1. **Install the package** No providers and no global stylesheet to register.
2. **Put rows and lists in a group** Declare the row template inside a `vdndGroup` element so every draggable finds its group.
3. **Apply the drop** The library never changes your data. `reorderItems()` applies the move to your signal.
[Read the quick start →](https://gultyayev.github.io/ngx-virtual-dnd/guide/start/quick-start.md)

```angular-html title="template.html" {13}
<div vdndGroup="tasks">
  <ng-template #taskTpl let-task>
    <div class="row" [vdndDraggable]="task.id">{{ task.title }}</div>
  </ng-template>

  <vdnd-sortable-list
    droppableId="backlog"
    [items]="tasks()"
    [itemHeight]="48"
    [containerHeight]="336"
    [itemIdFn]="taskId"
    [itemTemplate]="taskTpl"
    (drop)="onDrop($event)"
  />
</div>

<vdnd-drag-preview />
```
```ts title="quick-start.ts" {9}
export class QuickStartExampleComponent {
  readonly tasks = signal<Task[]>(
    Array.from({ length: 1000 }, (_, i) => ({ id: `task-${i + 1}`, title: `Task ${i + 1}` })),
  );

  readonly taskId = (task: Task): string => task.id;

  onDrop(event: DropEvent): void {
    reorderItems(event, this.tasks);
  }
}
```
