> 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.

# Quick start

This page builds a sortable list of 1,000 tasks. `<vdnd-sortable-list>` combines the drop target, the virtual scroller and the drop placeholder, so it is the right starting point for most apps.


Live exampleSortable list with 1,000 items[Open in new tab](/ngx-virtual-dnd/demo/examples/quick-start/)

## The code

```ts file="<root>/src/app/examples/quick-start/quick-start.ts"
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import {
  DragPreviewComponent,
  DraggableDirective,
  DropEvent,
  DroppableGroupDirective,
  reorderItems,
  VirtualSortableListComponent,
} from 'ngx-virtual-dnd';

interface Task {
  id: string;
  title: string;
}

@Component({
  selector: 'app-quick-start-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    VirtualSortableListComponent,
    DroppableGroupDirective,
    DraggableDirective,
    DragPreviewComponent,
  ],
  template: `
    <div vdndGroup="tasks">
      <!-- Declared inside vdndGroup, so every draggable inherits the group -->
      <ng-template #taskTpl let-task>
        <div class="row" [vdndDraggable]="task.id">{{ task.title }}</div>
      </ng-template>

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

    <!-- Renders the element that follows the pointer. Place it once. -->
    <vdnd-drag-preview />
  `,
})
export class QuickStartExampleComponent {
  // 1,000 items; only the visible rows are in the DOM.
  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);
  }
}

```

## What each part does

1. **`vdndGroup="tasks"`** names the group that draggables and drop targets belong to. Items can only move between drop targets in the same group. A group is required even for a single list; without one, dragging is disabled.
2. **The item template** is declared _inside_ the `vdndGroup` element. Angular templates resolve dependency injection where they are declared, so this is how each draggable finds its group.
3. **`[vdndDraggable]="task.id"`** makes each row draggable. The ID must equal what `itemIdFn` returns for that item, and it must sit on the template's root element.
4. **`<vdnd-sortable-list>`** renders the visible rows. `itemHeight` is the row height in pixels and `containerHeight` is the height of the scroll area.
5. **`(drop)`** fires when an item is dropped. The library never changes your data. `reorderItems()` applies the move to the `tasks` signal.
6. **`<vdnd-drag-preview />`** renders the element that follows the pointer. Place it once, anywhere in the template.

:::tip Keyboard support is built in
Every draggable can be focused with Tab
. Space
 picks it up, the arrow keys move it, and Space
 or Enter
 drops it. See [Accessibility](https://gultyayev.github.io/ngx-virtual-dnd/guide/features/accessibility.md)
.
:::
## Styles

The library does not style rows or the drop placeholder, so the code above renders plain text rows and an invisible placeholder. Add at least this CSS:

```css
/* In the component's styles: the row template is declared in its template */
.row {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  height: 48px; /* exactly itemHeight */
  padding: 0 16px;
  border-bottom: 1px solid #e2e7f0;
  background: #fff;
}

/* In a global stylesheet (styles.css): the library renders the placeholder */
.vdnd-drag-placeholder-visible {
  box-sizing: border-box;
  border: 1.5px dashed #0e9488;
  background: #e4f3f2;
}
```

:::warning Rows must be exactly itemHeight tall
With fixed heights, every row must be exactly `itemHeight` pixels tall, borders and padding included. Otherwise the rows on screen drift away from the positions the list calculates, and items drop in the wrong place.
:::

For rows that differ in height, see [Dynamic item heights](https://gultyayev.github.io/ngx-virtual-dnd/guide/features/dynamic-heights.md). [Styling](https://gultyayev.github.io/ngx-virtual-dnd/guide/features/styling.md) lists every class the library adds.

## Next steps

- [Core concepts](https://gultyayev.github.io/ngx-virtual-dnd/guide/essentials/core-concepts.md): what groups, draggables and droppables are, and the rules that keep them working
- [Multiple lists](https://gultyayev.github.io/ngx-virtual-dnd/guide/essentials/multiple-lists.md): move items between lists
- [Choosing a component](https://gultyayev.github.io/ngx-virtual-dnd/guide/essentials/choosing-a-component.md): when to use something other than `<vdnd-sortable-list>`
