Most slow Google Sheets come down to a few causes: volatile functions like NOW, INDIRECT and OFFSET that recalculate constantly, formulas copied down thousands of rows, open-ended references to whole columns, chains of IMPORTRANGE between files and too much conditional formatting. Fix those, split raw data from reports and archive old rows, and most sheets go from freezing to usable.
Every sheet I've used to run a business got slow eventually. Not because Google Sheets can't handle the data, but because of how the formulas grew over time: one quick fix copied down a column, another tab pulling from that tab, a script added to tidy it all up. Here's how I find what's slowing a sheet down and what I change, roughly in the order I check.
Before you change anything, make a copy of the file (File > Make a copy) and work there. Version history will save you either way, but a copy keeps the team working while you test.
1. Volatile functions
Some functions recalculate every time anything in the spreadsheet changes, or on a timer, whether or not their inputs changed. Those are the first thing I look for.
NOW()andTODAY()recalculate on every edit (and on a schedule if set in File > Settings > Calculation).RAND()andRANDBETWEEN()do the same.INDIRECT()andOFFSET()build references from text or positions, so Sheets can't tell what they depend on and recalculates them often.
One TODAY() in a header cell is harmless. TODAY() copied into 10,000 rows of =IF(C2<TODAY(), "Late", "") is not. Put =TODAY() in one cell, say Settings!B1, and point every formula at it:
=IF(C2 < Settings!$B$1, "Late", "")
For INDIRECT, there's almost always a direct reference that does the same job. If it's being used to pick a tab by name from a dropdown, CHOOSE or a SWITCH with direct ranges is faster and doesn't break when someone renames a tab. OFFSET used for "the last 30 rows" can usually become FILTER or QUERY with a condition on the date.
2. Formulas copied down every row
The pattern: someone writes a VLOOKUP in row 2 and drags it to row 20,000. Now there are 20,000 formulas, each looking up the same table separately. And when someone types a value over row 812, nobody notices.
One formula at the top of the column usually does the same work faster:
=ARRAYFORMULA(IF(A2:A = "", "", VLOOKUP(A2:A, Prices!A:B, 2, FALSE)))
or with newer functions:
=MAP(A2:A, LAMBDA(id, IF(id = "", "", XLOOKUP(id, Prices!A:A, Prices!B:B, "Not found"))))
The IF(A2:A = "", "", ...) part matters. Without it, the formula runs on every empty row to the bottom of the sheet.
For summary tabs, a single QUERY often replaces a whole grid of SUMIFS and COUNTIFS:
=QUERY(Sales!A:D, "select B, sum(D) where A >= date '2026-01-01' group by B order by sum(D) desc", 1)
3. Whole-column references in heavy formulas
A:A is convenient and in most simple formulas it's fine. The trouble comes when a heavy formula, like a lookup, a SUMPRODUCT or a COUNTIFS with several conditions, references full columns on a tab with tens of thousands of rows of formatting or blank cells below the data.
Two ways to fix it: keep your data tabs trimmed (see step 6), or use bounded ranges that match your real data, like A2:A5000, with some room to grow. For SUMPRODUCT especially, bounded ranges make a real difference.
4. IMPORTRANGE chains
IMPORTRANGE is how files talk to each other, and it's often the reason a sheet takes a minute to settle after opening. Each import has to fetch from another file. When File C imports from File B, which imports from File A, every open waits on the whole chain.
What I do:
- Import once per source. Pull the whole range you need into one hidden tab with a single
IMPORTRANGE, and point other formulas at that tab, instead of callingIMPORTRANGEinside dozens of formulas. - Break the chains. Have each file import from the original source, not from another copy.
- Import values, not live links, when live isn't needed. If yesterday's numbers are good enough, a small script that copies values on a schedule removes the import altogether:
function copyNightly() {
const source = SpreadsheetApp.openById('SOURCE_FILE_ID').getSheetByName('Data');
const values = source.getDataRange().getValues();
const target = SpreadsheetApp.getActive().getSheetByName('Imported');
target.clearContents();
target.getRange(1, 1, values.length, values[0].length).setValues(values);
}
Set that on a nightly time trigger and the file no longer waits on anything when it opens. The same idea works for IMPORTXML, IMPORTHTML and GOOGLEFINANCE.
5. Conditional formatting overload
Open Format > Conditional formatting and look at the list. On older sheets I often find dozens of rules, many duplicated because copying and pasting cells copies their rules too, each applied to slightly different ranges. Every rule is evaluated as you scroll and edit.
Delete the duplicates, merge rules that do the same thing into one rule on one range, and avoid custom formula rules that use INDIRECT, lookups or references to other tabs. A rule like =$D2="Overdue" is cheap. A rule that runs COUNTIF over another tab for every cell is not.
6. Unused rows, columns and tabs
Every cell counts toward the 10 million cell limit for the whole spreadsheet, filled or not. A tab with 200 columns and 50,000 rows is 10 million cells, the limit for the whole file, even if you only use a corner of it. Formatting and data validation applied to whole columns also extend into those cells.
Select the empty columns to the right of your data and the empty rows below it, right-click and delete them. Do the same on every tab. Delete old tabs nobody looks at, or move them to an archive file.
7. Raw data mixed with reports
The sheets that stay fast have the same shape: raw data on its own tabs with no formulas, calculations in their own tab, and reports or dashboards reading from those. When people type into the same tab that holds the heavy formulas, every keystroke recalculates the lot.
If a form feeds the sheet, leave the form response tab alone. Do your calculations on another tab that reads from it. That also protects you from the classic problem where form responses insert new rows and shift every formula's references.
8. Archive old rows
If the sheet has five years of orders but the team only works with this year, move older rows to an archive file. Reports can still read the archive when they need history. The working file gets lighter and every formula on it runs on less data. I usually set this up as a script that moves rows older than a set date once a month, with a log of what it moved.
9. Scripts that read one cell at a time
If the sheet is fast when you type but slow when a script runs, look at the script. A loop that calls getValue() and setValue() per cell makes a round trip to the sheet for each one. Reading the whole range with getValues(), changing the array in memory and writing it back with one setValues() call is usually many times faster. I wrote that up with examples in Apps Script limits.
Also check Extensions > Apps Script > Triggers for onEdit scripts. A script that runs on every single edit and does heavy work will make typing feel sluggish for everyone.
Want a second set of eyes?
If you've worked through this and the sheet is still slow, or you'd rather not touch formulas the business depends on, I do this kind of work under spreadsheet repair and speed-up, with a short report of what changed.
Questions people ask
How many rows can Google Sheets handle before it slows down?
A spreadsheet can hold up to 10 million cells, but speed depends far more on the formulas than on the row count. A plain data tab with 50,000 rows can be fine, while 5,000 rows of lookups using INDIRECT can freeze.
Does deleting empty rows make a sheet faster?
It helps, especially when formulas or formatting reach into those rows. Empty rows and columns still count toward the cell limit, and formulas that reference whole columns scan them.
Is ARRAYFORMULA faster than copying a formula down?
Often, yes, because one formula is easier for Sheets to manage than thousands of separate copies. It also can't be broken by someone typing over one row. Very heavy array formulas can still be slow, so test on your data.
Why is my sheet slow only when I open it?
Imports and volatile functions recalculate on open. IMPORTRANGE, IMPORTXML, GOOGLEFINANCE and NOW or TODAY all run again when the file loads, so a sheet full of them takes a while to settle.