Keep a self-updating contents file per folder
Auto-create a `_contents.md` Doc inside every Northwind folder, refreshed nightly.
Published Nov 13, 2025
Northwind’s project folders fill up over the life of a job — briefs, drafts, exports, contracts — and the only way to see what is inside one is to open it in Drive and scroll. That is fine until someone needs a quick overview without Drive access, or wants a printable index of a finished project, or is browsing the folder structure on their phone.
This script drops a _contents Doc into every folder it walks and keeps it
current. The Doc lists every file in that folder as a clickable link, refreshed
nightly so it never drifts out of date. Each folder ends up self-documenting: open
the contents Doc and you see the folder without opening the folder.
What you’ll need
- A root folder to index. The script walks it and every subfolder, and writes a contents Doc into each one.
- The folder’s ID — the string in its URL after
/folders/. - Nothing else. The script creates each
_contentsDoc itself using the Docs service, which every Google account has.
The script
// The Drive folder to index. The script recurses into every subfolder.
const ROOT_FOLDER_ID = '1abcRootFolderId';
// The name of the contents Doc placed inside each folder. It is also
// skipped when listing files, so it never lists itself.
const CONTENTS_DOC_NAME = '_contents';
/**
* Entry point. Walks the root folder and refreshes a contents Doc
* inside it and every subfolder beneath it.
*/
function refreshContentsFiles() {
walk(DriveApp.getFolderById(ROOT_FOLDER_ID));
}
/**
* Builds or refreshes the contents Doc for one folder, then recurses
* into its subfolders.
*/
function walk(folder) {
// 1. Find an existing contents Doc, or create a fresh one.
const existing = folder.getFilesByName(CONTENTS_DOC_NAME);
const hadDoc = existing.hasNext();
const doc = hadDoc
? DocumentApp.openById(existing.next().getId())
: DocumentApp.create(CONTENTS_DOC_NAME);
// 2. A newly created Doc lands in My Drive — move it into this folder.
if (!hadDoc) DriveApp.getFileById(doc.getId()).moveTo(folder);
// 3. Clear the Doc and give it a title naming the folder.
const body = doc.getBody();
body.clear();
body.appendParagraph('Contents of ' + folder.getName())
.setHeading(DocumentApp.ParagraphHeading.TITLE);
// 4. List every file in the folder as a linked bullet, skipping the
// contents Doc itself.
let count = 0;
const files = folder.getFiles();
while (files.hasNext()) {
const f = files.next();
if (f.getName() === CONTENTS_DOC_NAME) continue;
body.appendListItem('').appendText(f.getName()).setLinkUrl(f.getUrl());
count++;
}
// 5. Note an empty folder explicitly so the Doc is never blank.
if (count === 0) body.appendParagraph('(empty folder)');
doc.saveAndClose();
// 6. Recurse into every subfolder.
const subs = folder.getFolders();
while (subs.hasNext()) walk(subs.next());
}
How it works
refreshContentsFilesis the entry point — it just callswalkon the root folder.walklooks for an existing_contentsDoc in the folder. If one exists it reuses it; if not it creates a new Doc, which keeps the same URL across runs so links to it stay stable.- A freshly created Doc is born in My Drive, so the script moves it into the folder it belongs to.
- It clears the Doc body and writes a title naming the folder, so the Doc reads well even printed on its own.
- It walks the folder’s files and adds each as a bulleted, linked item — skipping the contents Doc so it never lists itself. An empty folder gets an explicit “(empty folder)” line instead of a blank page.
- It saves the Doc, then recurses into every subfolder so the whole tree ends up indexed.
Example run
Take a project folder Acme — Q3 campaign holding three files. After a run, the
_contents Doc inside it reads:
Contents of Acme — Q3 campaign
- Brief.gdoc
- Brand deck v4.pptx
- Shoot schedule.gsheet
Each bullet is a live link straight to the file. A subfolder Acme — Q3 campaign/Exports gets its own _contents Doc listing only that subfolder’s
files — every level of the tree is documented independently.
Trigger it
Run this nightly so each folder’s contents Doc reflects the day’s changes:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add Trigger.
- Choose
refreshContentsFiles, event source Time-driven, type Day timer, and a quiet hour such as 2am to 3am. - Save, and approve the authorisation prompt the first time.
Watch out for
- The script rebuilds each Doc from scratch every run. Do not hand-edit a
_contentsDoc expecting your notes to survive — anything you add is wiped on the next refresh. - It lists files but not subfolders. Each subfolder gets its own contents Doc
instead; if you want subfolders named in the parent Doc too, add a second loop
over
folder.getFolders(). - Creating and opening a Doc per folder is relatively slow. A few hundred folders is comfortable; many thousands may approach the six-minute runtime limit. If you hit it, index one subtree per run.
- Every refresh counts as an edit on every Doc, so the contents Docs themselves will appear in any Drive activity report. That is expected — just be aware they add noise to audits.
- The Docs created here are real Google Docs, despite the
.mdin the article title. There is no native Markdown file type in Drive; a Doc is the closest printable, linkable equivalent.
Related
Build a recurring file-delivery system
Drop a fresh report file into a Northwind client folder weekly — they don't even ask.
Updated Dec 15, 2025
Build a Drive search index in Sheets
Make Northwind's file metadata searchable in a Sheet — like Spotlight for Drive.
Updated Dec 7, 2025
Build a shared-folder onboarding kit
Auto-grant new Northwind hires the folders they need on day one.
Updated Nov 29, 2025
Route saved email attachments to project folders
File Gmail attachments into the right Northwind client folder based on subject keywords.
Updated Nov 25, 2025
Bundle a folder of images into one PDF
Combine Northwind scans into a single deliverable PDF using a generation service.
Updated Nov 17, 2025