Sometimes it is just more intuitive to add a row of data to a Google Sheet just below the header instead of at the bottom of the Sheet.
One instance when this comes to mind is when we need to monitor time-dependent data as it is being input programmatically based on a trigger through Google Apps Script. Seeing purchases or sign-ins at the top of your sheet live might be more convenient than at the bottom of the sheet.
In this tutorial, we will walk through how to insert a row and add a new row of data to that row. We’ll also look at how to maintain formatting during this process.
Then if you need to maintain a Named Range or Some formula columns, we’ll walk through that too.
Let’s dive in!
The Starter Sheet
If you want to try out the script examples directly or are playing along from the video tutorial, here is a copy of the starter script:
Add a Row of Data Below the Header with Apps Script – STARTER
The Video
The Basic Code to Add a Row after A Header
In this example, we have a simple Google Sheet where we need to add our data to a row just below the header.
The main run function is the insertRowAtTop_v1()
function. This contains all the code we need to complete our task.
The runsies_example1()
function is simply a sample function that simulates how you can integrate the insertRowAtTop_v1()
function into your own project.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/** * Add a Row of Data Below the Header with Apps Script * [This is an example]{@link https://yagisanatode.com} * @author Scott (Yagi) <yagisanatode@gmail.com> * @license Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) */ /** * Inserts a row at the top below the header. * @param {array<string>} data - 2d array [[id, date, email]] * @param {string} sheetName - the name of the selected sheet. * @param {number} targetRow - the row to insert the data in. */ function insertRowAtTop_v1(data, sheetName, targetRow) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName(sheetName); // Insert a row // NOTE show what happens if we use insertRowAfter and how it carries over formatting from the top. sheet.insertRowBefore(targetRow); sheet .getRange(targetRow, 1, 1, data[0].length) .setValues(data); SpreadsheetApp.flush(); } /** * Runs example1. * This function simulates how the insertRowAtTop funciton can be used. */ function runsies_example1(){ const targetRow = 2; const sheetName = "Example 1" // Dummy Data const myDate = new Date(); const myTime = myDate.getTime() // The id const data = [ [ myTime, myDate, `${myTime}@example.com` ] ] insertRowAtTop_v1(data, sheetName, targetRow) } |
If you have found the tutorial helpful, why not shout me a coffee ☕? I'd really appreciate it.
insertRowAtTop_v1()
Set up the function
The insertRowAtTop_v1()
function takes 3 arguments:
data
– This is a 2d array of data containing the values you want to enter into your new row.
Google Sheet data is presented as a 2d array in Google Apps Script where a row of data is an entire array and each row array is contained in an outer array. So for example, a sheet range that is 3 columns by 4 rows deep would look like this:
12345[["1A", "1B", "1C"],["2A", "2B", "2C"],["3A", "3B", "3C"]]sheetName
– This is the sheet tab name.targetRow
– This is the row where you want to insert your range. If your header data is deeper than one row, then you can change this value.
First, we call the getActiveSpreadsheet()
method from the Google Apps Script SpreadsheetApp class. You can use the openById()
or openByUrl()
methods.
Now that we have access to our sheet we can invoke the getSheet()
method taking the selected sheet as our parameter. For our example, the sheet will be ‘Example 1’ as indicated by the sheetName
parameter.
insert a row into the sheet
Our next task is to add a row to our sheet. We can do this with the .insertRowBefore(targetRow)
method. This method takes a target row as a parameter. For us, our argument for this parameter is row 2. Line 21
This method will insert a row above the target row.
Now you might have also seen the .insertRowAfter() method and may be wondering why this was not used. These insert methods reference their formatting (i.e. Font, font colour, borders, background colours etc.) from the target row. So if we used the ‘after’ method we would have the header formatting in our cell, which is not what we want.
add data to the new row
With the new row added, we can add the data to that range.
First, we need to get the range where we will add our data. We do this with the getRange() method. This can take a number of parameter formations, but we will use the 4-integer set. These include:
- Row Start – This will be the target row we set.
- Column Start – All our data will begin in Column A so we set this to one.
- Row Depth – We are only inserting one row of data so our depth will be one.
- Column Width – The width of the range will be determined by the width of the data that we will put into it. This means that if we get the length or total count of each cell item in the row then this will be the column width.
We can do this by using the JavaScript length property. Note that we need to get the length of the inner array (data[0].length
). This is done by referencing the first or zeroeth item in the array. Line 23
Now that we have our range we can set the values from our data parameter. We do this using the setValues() method. Line 24
Flush the Spreadsheet
While Google Apps Script will try and bundle your data together into one operation, a Flush can ensure that all operations on the spreadsheet are complete before any other operations are done on the sheet.
We achieve this with the SpreadsheetApp.flush() method.
runsies_example1()
This example function helps us to understand what parameters we can enter into insertRowAtTop_v1()
.
Here we set our target row to row 2 and the sheet name to ‘Example 1’.
For our dummy data, we will abuse the JavaScript Date constructor to generate the current date time stamp (Line 39). Then we will convert the date to a time in milliseconds with the getTime() method (Line 40).
We will then use the time as an ID, the date as, well… the date and build a dummy email from the time and a ‘@example.com’ string.
After that, add these variables into the function insertRowAtTop_v1()
call parameters.
Handling The Edge of a Named Range
In this example, we have added a named range to our data range. Perhaps to make it easier to handle the data in another operation in our sheet or in our script.
However, if we were to run our script in ‘Example 1’ above on this we would find that after inserting a new row, our Named Range will change its range. It will go from ‘Example 2’!A2:C15 to ‘Example 2’!A3:C16 as you can see in our Example 2 sheet DataSet named range in the image below. Each subsequent addition to the range will add another row to the DataSet
named range.
This will cause problems for us because it will miss the new data we add.
We need to fix this.
Check out the code for our updated function insertRowAtTop_v2()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/** * Inserts a row at the top below the header. * @param {array<string>} data - 2d array [[id, date, email]] * @param {string} sheetName - the name of the selected sheet. * @param {number} targetRow - the row to insert the data in. * @param {string} namedRangeName - the name of the affected Named Range */ function insertRowAtTop_v2(data, sheetName, targetRow, namedRangeName) { // Variables const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName(sheetName); const rowDepth = sheet.getLastRow() - targetRow + 3; // Add 1 to include depth + 1 for bottom space + 1 to remove the target row. const colWidth = data[0].length; // Insert a row // NOTE show what happens if we use insertRowAfter and how it carries over formatting from the top. sheet.insertRowBefore(targetRow); // Insert a row. sheet // Add the data to the new row. .getRange(targetRow, 1, 1, colWidth) .setValues(data); sheet .getNamedRanges() .find(namedRange => namedRange.getName() === namedRangeName) .setRange( sheet.getRange(targetRow, 1, rowDepth, colWidth) ) SpreadsheetApp.flush(); } |
Update insertRowAtTop_v2()
In this updated version we have added an extra parameter called namedRangeName
. In our example, this will be the DataSet
named range. Line 8
On lines 22-26, we need to update the named range, maintaining the existing row and column height, and column width but expanding its row depth.
Two new variables
Lines 13 and 14 add two new variables to our function and will be used to recalculate the range of our named range:
rowDepth
: This is the last row number subtracted by the target row + 3. In the example image above our target row is row 2. The last row with data is row 14. If we subtract 14 from 2 we get 12. However, we have 13 items in our sheet and our name range also included an empty space at the bottom – Which means we need to add 2. We will also add an extra row during our process so now our formula would look like this:
14 - 2 + 3 = sheet.getLastRow() - targetRow + 3
colWidth
: We will now need to get the column width twice from ourdata
parameter. Once when we get the range and, once when we get the width of the named range. Let’s call the length property only once (data[0].length
) and then reference the resulting value.
find the named range and update it
The only way we can update a named range with the SpreadsheetApp class is to first get an array of all named ranges in the spreadsheet. This is done with the getNamedRanges() method. Line 22
From here we can use the JavaScript Find method that will return the first matching item in the array. Line 23
.find(namedRange => namedRange.getName() === namedRangeName)
The Find method takes a function as an argument containing an iterator parameter that we set to namedRange
. This will run through each named range in the array until it finds our desired match.
For simplicity, we are using an arrow function here.
On each iteration, we can call the getName() method on the selected named range and compare that with our namedRangeName
parameter (e.g. ‘DataSet’). If there is a match Find will conclude its iteration and return the current named range.
Once we have our selected named range we can use the setRange() method to update the range (Line 25). This method takes another range construct as an argument (Line 26).
1 2 |
.setRange( sheet.getRange(targetRow, 1, rowDepth, colWidth) |
Here we set the start row to our target row in column A and then assign our row depth and column widths we generated earlier in the function.
Including preexisting formulas into the newly inserted row
You may have a scenario where you have some formulas in your sheet that you need to add to your data row when it is added to your Google Sheet.
In our ‘Example 3’ sheet tab, we have added two formula rows to our data set in columns D and E that we want to include in our new row entries.
Let’s take a look at the updated function now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/** * Inserts a row at the top below the header. * @param {array<string>} data - 2d array [[id, date, email]] * @param {string} sheetName - the name of the selected sheet. * @param {number} targetRow - the row to insert the data in. * @param {string} namedRangeName - the name of the affected Named Range * @param {boolean} hasFormulas - true if formula columns must be duplicated. */ function insertRowAtTop_v3(data, sheetName, targetRow, namedRangeName, hasFormulas = false) { // Variables const ss = SpreadsheetApp.getActiveSpreadsheet(); const sheet = ss.getSheetByName(sheetName); const rowDepth = sheet.getLastRow() - targetRow + 3; // Add 1 to include depth + 1 for bottom space + 1 to remove the target row. const colWidth = data[0].length; // Insert a row // NOTE show what happens if we use insertRowAfter and how it carries over formatting from the top. sheet.insertRowBefore(targetRow); // Insert a row. // If the sheet has a extra formulas to add. if(hasFormulas){ const maxCols = sheet.getMaxColumns() const rowBelow = sheet.getRange(targetRow + 1, 1, 1, maxCols) const rowAbove = sheet.getRange(targetRow, 1, 1, maxCols) rowBelow.copyTo(rowAbove) } sheet // Add the data to the new row. .getRange(targetRow, 1, 1, colWidth) .setValues(data); sheet .getNamedRanges() .find(namedRange => namedRange.getName() === namedRangeName) .setRange( sheet.getRange(targetRow, 1, rowDepth, colWidth) ) SpreadsheetApp.flush(); } |
Here we have included a new optional parameter hasFormulas
that is set to false by default. You can set this option to true if you wish to include formulas at the end of your row input. Line 9
Adding the formulas to the input
We have included an if condition after we created our row on lines 21-27. Here we check if the sheet has formulas at the end. If it does then get the total number of columns in the sheet with the getMaxColumns
method. Line 22
Next, we need to get the ranges for the newly inserted row (rowAbove
) and the row below it (rowBelow
). We will add one to the row to grab the row below and apply the maxCols
variable from line 22 to indicate the width of the range.
Finally, we will copy the row below in its entirety to the row above with the copyTo
method.
The format for this method is as follows:
sourceRange.copyTo(destinationRange)
Without any other optional parameters, this method will copy over the formatting and the formulas from the previous range. If the formulas are not set to absolute ranges (with the $ sign) then they will adjust automatically to the new cell range.
Conclusion
While there are probably still some edge cases that these examples don’t cover, I think that they will handle the majority of scenarios that you come across. The examples should also provide you with some understanding of the workings of the code to make your own modifications where you see fit.
It’s always interesting to hear how you apply the scripts to your own projects. Feel free to comment below.
Further Reading
If you are looking for an alternative approach to adding data to a sheet check out these tutorials:
- Copy and Paste Range Values from one Google Sheet into another with Google Apps Script
- Google Apps Script – Copy Selected Columns in One Sheet and Add Them To The Bottom of Different Selected Columns in Another
- Google Apps Script: Get the last row of a data range when other columns have content like hidden formulas and check boxes [updated Dec 2021]
- Google Apps Script Course – Part 1: 2D Array Data Transformation of Single Question Item Data to Total Count of Choices in Google Sheets
- Google Apps Script Course – Intro: 2D Array Data Transformation for Google Sheets
- Creating Unique Ranges from 2D Arrays in Google Apps Script
- Google Apps Script: Add and removed Google Sheets columns based on a search array
Need help with Google Workspace development?
Go something to solve bigger than Chat GPT?
I can help you with all of your Google Workspace development needs, from custom app development to integrations and security. I have a proven track record of success in helping businesses of all sizes get the most out of Google Workspace.
Schedule a free consultation today to discuss your needs and get started or learn more about our services here.
If you have found the tutorial helpful, why not shout me a coffee ☕? I'd really appreciate it.