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

# Dynamic item heights

When rows have different heights, set `[dynamicItemHeight]="true"`. `itemHeight` then becomes the **estimate** for rows that have not been rendered yet.


Live exampleList with variable row heights[Open in new tab](/ngx-virtual-dnd/demo/examples/dynamic-height/)

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

interface Note {
  id: string;
  title: string;
  body: string;
}

const SENTENCE = 'Rows are measured with a ResizeObserver as they render. ';

@Component({
  selector: 'app-dynamic-height-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    VirtualSortableListComponent,
    DroppableGroupDirective,
    DraggableDirective,
    DragPreviewComponent,
  ],
  template: `
    <div vdndGroup="notes">
      <ng-template #noteTpl let-note>
        <div class="row note" [vdndDraggable]="note.id">
          <span class="note-title">{{ note.title }}</span>
          <span class="note-body">{{ note.body }}</span>
        </div>
      </ng-template>

      <!-- itemHeight is only the estimate for rows that have not been measured yet -->
      <vdnd-sortable-list
        class="list"
        droppableId="notes"
        [items]="notes()"
        [itemHeight]="72"
        [dynamicItemHeight]="true"
        [containerHeight]="336"
        [itemIdFn]="noteId"
        [itemTemplate]="noteTpl"
        (drop)="onDrop($event)"
      />
    </div>

    <vdnd-drag-preview />
  `,
})
export class DynamicHeightExampleComponent {
  readonly notes = signal<Note[]>(
    Array.from({ length: 500 }, (_, i) => ({
      id: `note-${i + 1}`,
      title: `Note ${i + 1}`,
      body: SENTENCE.repeat((i % 4) + 1).trim(),
    })),
  );

  readonly noteId = (note: Note): string => note.id;

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

```

## How it works

- Rendered rows are measured with a `ResizeObserver`. If a row's content changes height later, the list updates.
- Heights are cached **by item ID** in each list, so they survive reordering. An item moved into another list is measured again there, starting from that list's estimate.
- Offsets are kept as prefix sums, so finding the row at a scroll position is a binary search (O(log n)). A measured height only invalidates the offsets after its row, and they are recomputed only as far as a lookup reaches, so measuring rows while scrolling a long list stays cheap.
- `dynamicItemHeight` and `itemHeight` can change at runtime. The list then starts again from the estimate and measures the rendered rows anew. Turning `dynamicItemHeight` off stops measuring.

Two rules from [Core concepts](https://gultyayev.github.io/ngx-virtual-dnd/guide/essentials/core-concepts.md#rules-that-fail-silently) matter most here. The draggable ID must equal the item ID, and `vdndDraggable` must be on the template's root element. The list uses both to find the element it measures.

## Choosing the estimate

Pick an estimate close to your typical row height. A poor estimate still works, but the scrollbar size and position shift more as rows are measured.

## Other components

The same input exists on every virtual container:

```angular-html
<vdnd-virtual-scroll [itemHeight]="80" [dynamicItemHeight]="true" ... />

<vdnd-virtual-content [itemHeight]="80" [dynamicItemHeight]="true" vdndDroppable="notes">
  ...
</vdnd-virtual-content>

<!-- *vdndVirtualFor directly inside a vdndScrollable, with no parent viewport -->
<ng-container
  *vdndVirtualFor="let note of notes(); itemHeight: 80; dynamicItemHeight: true; trackBy: trackById"
>
  ...
</ng-container>
```

Inside `<vdnd-virtual-content>` or `<vdnd-virtual-viewport>`, `*vdndVirtualFor` inherits `itemHeight` and `dynamicItemHeight` from its parent. Directly inside a `vdndScrollable`, it positions each row absolutely at its measured offset (see [VirtualForDirective](https://gultyayev.github.io/ngx-virtual-dnd/api/directives.md#virtualfordirective)).
