"Delete" is usually the wrong word. Archive the row to another tab (or workbook tab), then remove it from the working sheet — so audits, undo-ish recovery, and "what happened to order 1842?" stay possible.
What you'll need
- A source
Sheetand a 1-basedrowNumber(must be ≥ 2 — headers stay put) - A target
Sheetthat already has compatible columns (same header order is ideal)
Copy then delete
Apps Script has no atomic "move row." The safe pattern is: read values → appendRow on the target → deleteRow on the source. If you delete first and the append fails, the row is gone. Order matters.
How to use this snippet
function moveRowToSheet(sourceSheet, rowNumber, targetSheet) {
if (rowNumber < 2) {
throw new Error('moveRowToSheet: refuse to move header row');
}
var lastCol = sourceSheet.getLastColumn();
if (lastCol < 1) {
throw new Error('moveRowToSheet: source sheet has no columns');
}
var values = sourceSheet.getRange(rowNumber, 1, 1, lastCol).getValues()[0];
targetSheet.appendRow(values);
sourceSheet.deleteRow(rowNumber);
return targetSheet.getLastRow();
}
Tips:
- When moving many rows from the bottom up, delete from the highest row number first so earlier indexes don't shift under you.
- This copies values only — not notes, formatting, or data-validation. If you need formatting, use
copyToon the range before delete. - Wrap multi-row archive jobs in
withDocumentLockso two editors don't race the same deletes.
Example
function archiveDoneRow(rowNumber) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var live = ss.getSheetByName('Tasks');
var archive = ss.getSheetByName('Archive') || ss.insertSheet('Archive');
// Optional: ensure Archive has the same headers once
if (archive.getLastRow() < 1) {
var headers = live.getRange(1, 1, 1, live.getLastColumn()).getValues()[0];
archive.appendRow(headers);
archive.setFrozenRows(1);
}
return moveRowToSheet(live, rowNumber, archive);
}
Happy Coding!
