appscript.dev
Automation Beginner Docs Sheets Drive

Build a one-click Doc template library

Spin a new Doc from any approved template via a custom menu in the master sheet.

Published Dec 7, 2025

Northwind has a tidy set of approved Doc templates — proposals, statements of work, meeting notes — but starting a new one was a chore. People hunted through Drive for the right file, copied it, renamed the copy, and half the time grabbed an outdated version or edited the master by mistake.

This script turns the template folder into a menu. When the master Sheet opens, it lists every Doc in a designated Drive folder as a menu item. Click one and it copies the template, datestamps the name, and shows a link to the fresh Doc — no hunting, no accidental edits to the master.

What you’ll need

  • A Drive folder holding your approved templates. Every Google Doc inside it becomes a menu item, so keep only real templates in there.
  • A master Google Sheet with a bound Apps Script project.
  • The template folder’s ID, pasted into the config block.

The script (bound to the Sheet)

// Drive folder whose Docs are offered as templates.
const TEMPLATES_FOLDER = '1abcTemplatesFolderId';

/**
 * Builds the "Templates" menu when the spreadsheet opens, with one
 * item per Google Doc in the templates folder. Each item is wired
 * to a generated function that copies that specific template.
 */
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  const menu = ui.createMenu('Templates');

  // 1. List every Google Doc in the templates folder.
  const files = DriveApp.getFolderById(TEMPLATES_FOLDER)
    .getFilesByType(MimeType.GOOGLE_DOCS);

  let count = 0;
  while (files.hasNext()) {
    const file = files.next();

    // 2. Give each template a unique handler name based on its file ID.
    const fnName = `create_${file.getId()}`;

    // 3. Define that handler on the global scope so the menu can call it.
    this[fnName] = () => copyTemplate(file.getId());

    // 4. Add the template to the menu under its file name.
    menu.addItem(file.getName(), fnName);
    count++;
  }

  if (count === 0) {
    menu.addItem('(no templates found)', 'noop');
  }
  menu.addToUi();
}

/**
 * Copies a template Doc, datestamps the copy's name, and shows the
 * user a link to the new file.
 *
 * @param {string} fileId - The Drive ID of the template to copy.
 */
function copyTemplate(fileId) {
  const file = DriveApp.getFileById(fileId);
  const today = new Date().toISOString().slice(0, 10);

  // Copy the template with today's date appended to the name.
  const copy = file.makeCopy(`${file.getName()} — ${today}`);

  SpreadsheetApp.getUi()
    .alert('Created', `Doc: ${copy.getUrl()}`, SpreadsheetApp.getUi().ButtonSet.OK);
}

/** Placeholder handler used when the templates folder is empty. */
function noop() {}

How it works

  1. onOpen runs each time the master Sheet is opened and starts a menu called Templates.
  2. It lists every Google Doc in TEMPLATES_FOLDER with getFilesByType, so the menu always reflects what is actually in the folder.
  3. For each template it builds a handler name from the file’s ID — guaranteed unique — and attaches a function to the global scope (this[fnName]). Apps Script menus can only call a function by name, so the handler must exist globally.
  4. Each generated handler calls copyTemplate with that file’s ID, and the template is added to the menu under its file name.
  5. If the folder holds no Docs, a disabled-looking placeholder item is shown instead of an empty menu.
  6. copyTemplate copies the chosen file, appends today’s date in YYYY-MM-DD form to the name, and pops an alert with a clickable link to the new Doc.

Example run

With three Docs in the templates folder, opening the master Sheet shows:

Templates
 ├─ Project proposal
 ├─ Statement of work
 └─ Meeting notes

Clicking Statement of work on 7 December 2025 creates a Drive file named:

Statement of work — 2025-12-07

and shows an alert with a link straight to it. The original template is untouched.

Run it

The library sets itself up:

  1. Reload the master Sheet so onOpen builds the Templates menu.
  2. Open the menu and click any template.
  3. Follow the link in the alert to your new Doc.

Add or remove a Doc in the folder and the menu updates on the next reload — no code change needed.

Watch out for

  • onOpen is a simple trigger and runs on every open. A folder with dozens of templates makes the menu slow to build; keep the folder lean.
  • The copy lands in the user’s Drive root, not the templates folder — which is intentional, so the folder stays clean. Pass a folder to makeCopy if you want copies collected somewhere specific.
  • Building handlers with this[fnName] only works because onOpen runs in the global scope. Do not move that loop inside another function.
  • Only Google Docs are listed. Templates saved as Word .docx files are skipped by MimeType.GOOGLE_DOCS — convert them to Docs first.
  • The menu is a snapshot from when the Sheet was opened. Add a template while the Sheet is open and it will not appear until the next reload.

Related