Lattice Grid Buy a licence

demo D86

Copy and Paste Between a Data Grid and Excel in htmx

Copy and paste against Excel and Sheets

clipboard interop

This grid copies and pastes ranges against Excel and Google Sheets, keeping rows and columns intact across the boundary. It lets people move data between the grid and a spreadsheet the way they already do, so the grid feels like one rather than just resembling one.

Building…
Loading a live grid…

This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">

<!-- A real, server-rendered table: readable with JavaScript off. data-field on
     each header names the column that cell becomes, and data-id on each row is
     the key the grid carries it under. -->
<table id="clipboard-table" data-lattice-grid>
  <thead>
    <tr>
      <th data-field="id">Order</th>
      <th data-field="reference">Reference</th>
      <th data-field="team">Team</th>
      <th data-field="stage">Stage</th>
      <th data-field="due">Due</th>
      <th data-field="circuits">Circuits</th>
      <th data-field="budget">Budget</th>
    </tr>
  </thead>
  <tbody>
    <tr data-id="WO-00001"><td>WO-00001</td><td>REQ-9090</td><td>central.birmingham</td><td>done</td><td>2026-01-01</td><td>27</td><td>8580</td></tr>
    <tr data-id="WO-00002"><td>WO-00002</td><td>INC-7104</td><td>south.london</td><td>done</td><td>2026-02-02</td><td>20</td><td>23160</td></tr>
    <tr data-id="WO-00003"><td>WO-00003</td><td>REQ-7362</td><td>south.london</td><td>blocked</td><td>2026-03-03</td><td>30</td><td>17685</td></tr>
    <!-- the rest of the rows, rendered by your server -->
  </tbody>
</table>

<script type="module">
  import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/htmx.esm.min.js';

  // The markup carries the rows; a server-rendered table's column types are
  // declared in the config hydrateTable takes. The grid replaces the table
  // where it stood.
  const grid = hydrateTable(document.getElementById('clipboard-table'), {
    selection: { mode: 'multiple', ranges: true },
    edit: true,
    // The clipboard action copies the selected block as tab-separated text, ready
    // to drop into a spreadsheet, and a paste back writes the cells it covers.
    toolPanel: { side: 'left', panels: ['columns'], actions: ['clipboard', 'export', 'undo', 'redo'], exportName: 'work-orders' },
    columns: [
      { field: 'id', title: 'Order', layout: { width: 120, pin: 'start' } },
      { field: 'reference', title: 'Reference', layout: { width: 130 } },
      { field: 'team', title: 'Team', filter: { type: 'set' }, edit: true },
      { field: 'stage', title: 'Stage', filter: { type: 'set' }, edit: true },
      { field: 'due', title: 'Due', type: 'date', layout: { width: 140 }, edit: true },
      { field: 'circuits', title: 'Circuits', type: 'number', layout: { width: 110 }, edit: true },
      { field: 'budget', title: 'Budget', type: 'number', layout: { width: 140 },
        format: { style: 'currency', currency: 'GBP' }, total: 'sum', edit: true },
    ],
  });
</script>

Copying and pasting a range against Excel and Sheets

Clipboard interop decides whether a JavaScript data grid feels like a spreadsheet or merely looks like one. A developer reaches for it as soon as users move data between the grid and Excel or Google Sheets: selecting a range, copying it, and having the paste land as tab-separated columns and newline-separated rows, in the order the source application wrote them, rather than as one opaque string. Lattice Grid’s clipboard interop reads and writes that tab-separated form directly, so a copied range pastes into Sheets as real columns, and a range copied from Excel pastes into the grid cell by cell, run through each destination column’s parser and validation before the write commits.

Paste targets a range the same shape as the copied block, starting from the active cell, clipped at the edge of the data if the destination is smaller than the source. A value that fails a column’s validation is rejected on its own, so one bad cell does not roll back the rest of the paste. The interaction runs through the same edit pipeline as typing into a cell, so a paste is one entry on the undo stack regardless of how many cells it touched, and a screen reader announces the count of cells changed once, at the end. See the step-by-step how to copy and paste between a grid and Excel guide for a runnable example and the source.

Does copy and paste work between Lattice Grid and Excel?

Yes. Copying a range writes tab-separated values to the system clipboard, the form Excel and Google Sheets both read as columns and rows. Pasting reads the same format back, splitting on tabs and newlines and writing each value through the destination column’s type and validation.