Move from container-bound to standalone scripts
When to migrate a Northwind script from a Sheet to a standalone project — and how.
Published Aug 12, 2025
Most Apps Script projects start life container-bound — created from a
spreadsheet’s Extensions → Apps Script menu, living inside that one file. It is
the quickest way to begin, and for a script that only ever touches its host
sheet it is perfectly fine.
A standalone script, by contrast, is its own project at script.google.com
with no host document. It can reach any number of files, be shared
independently, and be published as a library or add-on. Knowing when to migrate
from one to the other — and how to do it cleanly — saves a lot of pain later.
Bound vs standalone
The two project types differ in a few important ways.
| Aspect | Container-bound | Standalone |
|---|---|---|
| Lives inside | One spreadsheet (or doc/form) | Its own project, no host |
| Default document | getActive() returns the host | getActive() returns null |
| Simple triggers | onOpen, onEdit work directly | Not available — use installable triggers |
| Sharing | Copied with the host file | Shared on its own, reusable |
| Publishing | Limited | Can become a library or editor add-on |
Why migrate
Stay container-bound while the script genuinely belongs to one file. Migrate when any of the following becomes true.
- You need to share logic across many spreadsheets. A standalone script can open and operate on any file by ID, so one project serves dozens of sheets instead of being copied into each.
- The script outlives the source file. If the host spreadsheet might be archived, deleted, or replaced, a script bound to it dies with it. A standalone project is independent of any single document.
- You want to publish it. Editor add-ons and reusable libraries must be standalone projects. A container-bound script cannot be published that way.
How
Migration is a manual copy plus a handful of code changes. There is no built-in “convert” button, so work through these steps deliberately.
-
Copy the code by hand. The editor’s
File → Save a copycopies the whole container, not just the script — that is not what you want. Open the standalone project and paste the code in directly. -
Create the new project. Go to
script.google.comand create a new project. This is the standalone home for the migrated code. -
Replace active-document calls with explicit IDs. Swap
SpreadsheetApp.getActive()forSpreadsheetApp.openById(ID), since a standalone script has no active document to fall back on.// Before — container-bound: relies on the host spreadsheet. // const sheet = SpreadsheetApp.getActive().getSheets()[0]; // After — standalone: open the target explicitly by its file ID. const sheet = SpreadsheetApp.openById('1abcYourSheetId').getSheets()[0]; -
Convert simple triggers to installable ones. Simple triggers like
onOpenandonEditonly fire for a script bound to the document. From a standalone project, create an installable trigger on the source sheet instead.// A standalone script cannot use a simple onEdit; install one // that targets the specific spreadsheet by ID. function installEditTrigger() { const ss = SpreadsheetApp.openById('1abcYourSheetId'); ScriptApp.newTrigger('handleEdit').forSpreadsheet(ss).onEdit().create(); } -
Re-add configuration and triggers. Script Properties do not travel with copied code, and triggers never copy between projects. Re-enter any properties and run your trigger setup in the new project.
Watch out for
getActiveSpreadsheet()returnsnull. This is the most common breakage after migrating. There is no active document in a standalone project — every document access must be an explicitopenById().- Simple triggers silently stop firing.
onOpenandonEditwill not run from a standalone script. If an automation depended on them, it is broken until you install the equivalent installable trigger. - The new project needs its own OAuth authorisation. A standalone script is a separate identity; the first run will prompt for permissions afresh, sometimes a broader set than the bound version needed.
- Script Properties do not come along. Pasting code copies only code. Any configuration stored in properties must be re-entered in the new project.
- Hard-coded IDs become a maintenance point. Once you depend on
openById(), a moved or recreated file breaks the script. Keep file IDs in Script Properties rather than scattered through the code. - Custom functions stop working. A spreadsheet custom function (called from a cell) must be container-bound. If the script provided one, that part cannot move to a standalone project.