All posts
Blog / Tutorial
Tutorial

Copy a Row to Another Sheet in Google Apps Script (3 Methods)

Three copy-paste ready methods — by row number, by condition, and on edit trigger — plus how to move rows and fix the most common errors.

Copy a Row to Another Sheet in Google Apps Script (3 Methods)

If you've ever needed to move or duplicate data between sheets automatically, you're in the right place. In Google Apps Script, copying a row to another sheet is one of the most common automation tasks — and there are several ways to do it depending on your exact needs.

This guide covers 3 methods to copy a row to another sheet using Google Apps Script, with copy-paste ready code for each. Whether you want to copy based on a condition, copy on edit, or copy an entire row in bulk, there's a method here for you.

Why Copy Rows Between Sheets with Apps Script?

Manual copy-pasting between sheets breaks the moment you add new data. Apps Script automates this permanently. Common use cases include moving completed tasks from an Active sheet to an Archive sheet, routing form responses to separate sheets by category, copying approved entries to a summary sheet, and syncing data from one tab to another based on a status column.

Before You Start: Open the Script Editor

Open your Google Sheet, click Extensions → Apps Script, delete any placeholder code in the editor, paste whichever method you need below, then click Save and Run. You'll be prompted to authorize the script on first run — that's expected.

Method 1: Copy a Specific Row by Row Number

This is the simplest approach. Use it when you know exactly which row you want to copy, or when you're building a more complex script and need a reliable copy function.

function copyRowToAnotherSheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sourceSheet = ss.getSheetByName("Sheet1");
  const targetSheet = ss.getSheetByName("Sheet2");

  const rowNumber = 3; // Change this to the row you want to copy
  const lastCol = sourceSheet.getLastColumn();

  // Get all values in the row
  const rowData = sourceSheet.getRange(rowNumber, 1, 1, lastCol).getValues();

  // Append to the next available row in the target sheet
  targetSheet.getRange(targetSheet.getLastRow() + 1, 1, 1, lastCol).setValues(rowData);
}

This reads all values from row 3 of Sheet1 and appends them to the next empty row in Sheet2. Use it for quick one-off copies, or as a utility function inside a larger automation.

Method 2: Copy Rows Based on a Condition (Most Useful)

This is the most practical method. It loops through all rows in your source sheet and copies any row that meets a condition — like a status column saying "Done" or "Approved".

function copyRowsBasedOnCondition() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sourceSheet = ss.getSheetByName("Sheet1");
  const targetSheet = ss.getSheetByName("Archive");

  const conditionColumn = 4;  // Column D — change to your status column
  const conditionValue = "Done"; // Change to whatever triggers the copy

  const data = sourceSheet.getDataRange().getValues();
  const rowsToCopy = [];

  for (let i = 1; i < data.length; i++) { // Start at 1 to skip header row
    if (data[i][conditionColumn - 1] === conditionValue) {
      rowsToCopy.push(data[i]);
    }
  }

  if (rowsToCopy.length === 0) {
    Logger.log("No matching rows found.");
    return;
  }

  // Write all matching rows at once (much faster than row-by-row)
  const startRow = targetSheet.getLastRow() + 1;
  targetSheet.getRange(startRow, 1, rowsToCopy.length, rowsToCopy[0].length).setValues(rowsToCopy);

  Logger.log(`Copied ${rowsToCopy.length} row(s) to Archive.`);
}
Writing all rows at once with a single setValues() call is far faster than looping and writing row-by-row. This matters a lot when dealing with hundreds of rows.

This reads all rows from Sheet1, finds every row where column D equals "Done", and copies all matching rows to the Archive sheet in one batch write. Use it for filtering completed tasks, archiving entries, and routing by status.

Method 3: Copy a Row Automatically on Edit (Trigger-Based)

This method fires automatically every time someone edits the sheet. When the value in a specific column changes to your trigger value, the row gets copied to another sheet instantly — no manual running required.

function onEdit(e) {
  const triggerColumn = 5;       // Column E — the column to watch
  const triggerValue = "Approved"; // The value that triggers the copy
  const sourceSheetName = "Requests";
  const targetSheetName = "Approved";

  const sheet = e.source.getActiveSheet();

  // Only run on the correct sheet and column
  if (sheet.getName() !== sourceSheetName) return;
  if (e.range.getColumn() !== triggerColumn) return;
  if (e.value !== triggerValue) return;

  const row = e.range.getRow();
  const ss = e.source;
  const targetSheet = ss.getSheetByName(targetSheetName);
  const lastCol = sheet.getLastColumn();

  const rowData = sheet.getRange(row, 1, 1, lastCol).getValues();
  targetSheet.getRange(targetSheet.getLastRow() + 1, 1, 1, lastCol).setValues(rowData);

  Logger.log(`Row ${row} copied to ${targetSheetName}`);
}

This watches column E of the Requests sheet. When someone changes a cell in that column to "Approved", the entire row is instantly copied to the Approved sheet. It does nothing for edits in other columns or other sheets.

The onEdit trigger runs automatically — you do not need to set up a separate trigger in Apps Script. It's a simple trigger that Google runs for you whenever a cell is edited.

Use this method for approval workflows, real-time data routing, and instant archiving.

How to Copy a Row AND Delete It from the Source Sheet

Sometimes you want to move a row rather than copy it. Add a delete step after copying:

function moveRowToAnotherSheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sourceSheet = ss.getSheetByName("Sheet1");
  const targetSheet = ss.getSheetByName("Sheet2");

  const rowNumber = 3; // Row to move
  const lastCol = sourceSheet.getLastColumn();

  // Copy the row
  const rowData = sourceSheet.getRange(rowNumber, 1, 1, lastCol).getValues();
  targetSheet.getRange(targetSheet.getLastRow() + 1, 1, 1, lastCol).setValues(rowData);

  // Delete from source
  sourceSheet.deleteRow(rowNumber);
}

Note: deleteRow() shifts all rows below it up by one. If you're deleting multiple rows in a loop, iterate from the bottom up to avoid skipping rows.

Common Errors and Fixes

"Cannot find sheet 'Sheet2'" — The sheet name in your code doesn't match exactly. Sheet names are case-sensitive. Check for extra spaces too.

Copied row has the wrong number of columns — Make sure lastCol reflects your source sheet, not the target. Call getLastColumn() on the correct sheet object.

onEdit isn't firing — Make sure your function is named exactly onEdit — that's the reserved name Apps Script uses. Also confirm you're editing the correct sheet.

Script times out on large datasets — Use batch reads and writes (like Method 2 above) instead of reading/writing one cell at a time inside a loop. This alone can make scripts 10–100x faster.

Which Method Should You Use?

Copy a known row manually → Method 1. Copy all rows matching a filter → Method 2. Copy automatically when a cell changes → Method 3. Move (copy + delete) a row → the move variant above.

Take It Further

Once you can copy rows between sheets, the next step is usually adding conditions, sending notifications, or scheduling the automation to run on a timer. Combine Method 2 with a time-driven trigger so it runs every morning automatically, or after copying, email yourself or your team when rows move.

Or skip the scripting entirely — Google Apps Script Copilot writes all of this code for you in one prompt, directly in your browser. Free to try.

Written by Hassan Raza
Founder of GS Copilot · GS Copilot

The team is small enough that you can usually reply to a post and get the actual writer. Try it — send us a note.