External drop zones (row drag)

Drag a row out of the grid onto any element - an Archive or Delete bucket - via the rowDropZone action. The row leaves the grid and the zone's onDrop handles it. In-grid reorder still works.

A live, editable Svelte 5 data grid example from the SvGrid gallery (Rows & Cells). See the SvGrid documentation for the full API.

About this example

Dropping a Svelte 5 data grid row onto any element outside the grid. The rowDropZone action marks an Archive and a Delete bucket as targets for managed row drags: the dragged task leaves the grid and the zone's onDrop handles it. The grid keeps rowDragManaged so in-grid reorder still works, and a rowDragGroup that the zones match on.

Managed row drags can be dropped onto ANY element outside the grid via the rowDropZone action. Here, drag a task out of the grid onto Archive or Delete: the row leaves the grid, and the zone's onDrop handles it. The grid keeps rowDragManaged (so in-grid reorder still works) and a rowDragGroup the zones match on.

Imports, features and API used

Imports: @svgrid/grid

Columns: title (Task), priority (Priority)

Frequently asked questions

How do I make an element accept rows from the grid?

Apply use:rowDropZone={{ group, onDrop }} to it with the same group string as the grid's rowDragGroup. When a row is released over the element, the grid removes it and onDrop receives an event with the row.

Can the same grid still reorder its own rows?

Yes. rowDragManaged stays on, so dropping inside the grid reorders and dropping on a zone removes; the target decides which happens.

How do I show the zone as active during a drag?

The action adds sv-grid-row-dropzone-over to the element while a matching drag hovers it, or a class of your own through overClass, which the demo styles as a highlighted bucket.

Related documentation

Related articles

Source code (184-external-drop-zone.svelte)

<!-- Documented in: docs/help/rows/row-dragging.md -->
<script lang="ts">
  /**
   * 184. External row-drag drop zones
   * ---------------------------------
   * Managed row drags can be dropped onto ANY element outside the grid via the
   * `rowDropZone` action. Here, drag a task out of the grid onto Archive or
   * Delete: the row leaves the grid, and the zone's `onDrop` handles it. The
   * grid keeps `rowDragManaged` (so in-grid reorder still works) and a
   * `rowDragGroup` the zones match on.
   */
  import { SvGrid, rowDropZone, type ColumnDef } from '@svgrid/grid'

  type Task = { id: number; title: string; priority: 'High' | 'Medium' | 'Low' }

  let tasks = $state<Task[]>([
    { id: 1, title: 'Design empty states', priority: 'Medium' },
    { id: 2, title: 'Keyboard nav audit', priority: 'High' },
    { id: 3, title: 'Dark theme polish', priority: 'Low' },
    { id: 4, title: 'CSV export edge cases', priority: 'Medium' },
    { id: 5, title: 'Virtualization stress test', priority: 'High' },
  ])
  let archived = $state<Task[]>([])
  let lastAction = $state('Drag a row onto Archive or Delete.')

  const columns: ColumnDef<any, Task>[] = [
    { field: 'title', header: 'Task', width: 240 },
    { field: 'priority', header: 'Priority', width: 110 },
  ]
</script>

<section class="flex flex-col flex-1 min-h-0 gap-3">
  <p class="text-sm shrink-0" style="color: var(--sg-fg);">
    Drag a task by its grip out onto a <strong>drop zone</strong>. The row leaves
    the grid and the zone's <code>onDrop</code> runs - Archive keeps it, Delete
    discards it. In-grid reorder still works.
  </p>

  <div class="edz-status shrink-0">{lastAction}</div>

  <div class="edz-layout flex-1 min-h-0">
    <div class="edz-grid">
      <SvGrid responsive={true}
      columnResize
        data={tasks}
        columns={columns}
        showRowNumbers={true}
        rowDragManaged={true}
        rowDragGroup="tasks"
        rowHeight={40}
        containerHeight="100%"
        fitColumns={true}
      />
    </div>

    <div class="edz-zones">
      <div
        class="edz-zone edz-archive"
        use:rowDropZone={{
          group: 'tasks',
          onDrop: (e) => { archived = [...archived, e.row as Task]; lastAction = `Archived "${(e.row as Task).title}"` },
        }}
      >
        <div class="edz-zone-title">📥 Archive</div>
        <div class="edz-zone-hint">Drop to archive ({archived.length})</div>
        {#if archived.length}
          <ul class="edz-archived">
            {#each archived as t (t.id)}<li>{t.title}</li>{/each}
          </ul>
        {/if}
      </div>

      <div
        class="edz-zone edz-delete"
        use:rowDropZone={{
          group: 'tasks',
          onDrop: (e) => { lastAction = `Deleted "${(e.row as Task).title}"` },
        }}
      >
        <div class="edz-zone-title">🗑️ Delete</div>
        <div class="edz-zone-hint">Drop to remove</div>
      </div>
    </div>
  </div>
</section>

<style>
  .edz-status {
    font-size: 12px; color: var(--sg-fg);
    background: var(--sg-header-bg); border: 1px solid var(--sg-border);
    border-radius: 6px; padding: 8px 12px;
  }
  .edz-layout { display: grid; grid-template-columns: 1fr 240px; gap: 16px; }
  .edz-grid { min-height: 0; }
  .edz-zones { display: flex; flex-direction: column; gap: 12px; }
  .edz-zone {
    flex: 1; display: flex; flex-direction: column; gap: 4px;
    border: 2px dashed var(--sg-border, #cbd5e1); border-radius: 10px;
    padding: 14px; text-align: center; color: var(--sg-muted, #64748b);
    transition: border-color 120ms ease, background-color 120ms ease;
  }
  .edz-zone-title { font-size: 15px; font-weight: 700; color: var(--sg-fg, #0f172a); }
  .edz-zone-hint { font-size: 11px; }
  .edz-delete { border-color: color-mix(in oklab, #ef4444 40%, var(--sg-border)); }
  .edz-archived {
    list-style: none; margin: 6px 0 0; padding: 0; text-align: left;
    font-size: 11px; color: var(--sg-fg); overflow: auto;
  }
  .edz-archived li { padding: 2px 6px; border-radius: 4px; background: color-mix(in oklab, #10b981 10%, transparent); margin-bottom: 3px; }
</style>

View this example on GitHub

More Rows & Cells examples

  • Managed row dragging (grid-to-grid) - Reorder rows by dragging their grip, or move a row from one grid into another - both grids share a rowDragGroup, so the row leaves the source and lands in the target. The grid mutates its own data on drop and fires onRowDragEnd on the receiver.
  • Custom cells + themes - Avatars, sparklines, progress bars, density toggle, dark mode, full a11y.
  • Sparkline cells - In-cell mini charts as a first-class column type: set `sparkline` on a number-array column and the grid paints an inline SVG. Line, area, bar (with +/- coloring), and win/loss - no chart library, no custom snippet.
  • Conditional formatting (engine) - Excel-style value-driven cell coloring as a declarative `conditionalFormats` engine prop: gradient heat maps (alpha ramp, zero-centred, banded, column-comparison), in-cell data bars, icon sets, and predicate rules - scoped per column, no per-cell snippet.
  • Conditional styling - Support-ticket triage board: rowClass highlights SLA breach + at-risk rows with side-bar accents; cellClass paints priority pills, status badges, agent-load progress bars, and CSAT highlights.