Google Docs + Apps Script is a magic combo for automating writing workflows, templates, formatting, and reporting.
https://github.com/lsvekis/100-Useful-Google-Docs-Apps-Script-Functions
In this post, you’ll get 100 tiny utility functions you can drop into your own projects. Each one is:
- Focused on Google Docs use-cases
- Small and reusable
- Uses built-in
DocumentAppandDriveApp(no advanced APIs required)
Tip: Create a single
.gsfile calleddocsHelpers.gsand paste all the functions there. Then call them from your main script files.
🧱 Section 1 – Core Setup & Helpers
1. Get the active document
function getActiveDoc_() {
return DocumentApp.getActiveDocument();
}
2. Get the body of the active document
function getBody_() {
return DocumentApp.getActiveDocument().getBody();
}
3. Get document title
function getDocTitle_() {
return DocumentApp.getActiveDocument().getName();
}
4. Set document title
function setDocTitle_(newTitle) {
DocumentApp.getActiveDocument().setName(newTitle);
}
5. Log basic document info
function logDocInfo_() {
const doc = DocumentApp.getActiveDocument();
console.log('ID:', doc.getId());
console.log('Title:', doc.getName());
console.log('URL:', doc.getUrl());
}
📄 Section 2 – Create & Open Documents
6. Create a new document with title
function createDoc_(title) {
return DocumentApp.create(title || 'New Document');
}
7. Create a doc and write first line
function createDocWithText_(title, firstLine) {
const doc = DocumentApp.create(title);
doc.getBody().appendParagraph(firstLine || 'Hello from Apps Script!');
return doc;
}
8. Open a document by ID
function openDocById_(id) {
return DocumentApp.openById(id);
}
9. Open a document by URL
function openDocByUrl_(url) {
return DocumentApp.openByUrl(url);
}
10. Duplicate an existing document
function copyDoc_(docId, newName) {
const file = DriveApp.getFileById(docId);
return file.makeCopy(newName || (file.getName() + ' (Copy)'));
}
✍️ Section 3 – Basic Text Insertion
11. Append a paragraph to the end of the doc
function appendParagraph_(text) {
getBody_().appendParagraph(text);
}
12. Insert text at the top
function insertAtTop_(text) {
const body = getBody_();
body.insertParagraph(0, text);
}
13. Append plain text (no new paragraph)
function appendTextToLastParagraph_(text) {
const body = getBody_();
const last = body.getParagraphs().slice(-1)[0];
last.appendText(text);
}
14. Append a heading
function appendHeading_(text, level) {
const body = getBody_();
const para = body.appendParagraph(text);
const style = DocumentApp.ParagraphHeading['HEADING' + (level || 1)];
para.setHeading(style);
}
15. Insert a horizontal rule
function insertHorizontalRule_() {
getBody_().appendHorizontalRule();
}
🎨 Section 4 – Text Styling
16. Make all text bold
function makeAllBold_() {
const body = getBody_();
body.editAsText().setBold(true);
}
17. Make selection bold
function toggleSelectionBold_() {
const doc = getActiveDoc_();
const range = doc.getSelection();
if (!range) return;
range.getRangeElements().forEach(re => {
const el = re.getElement();
if (el.editAsText) {
const text = el.editAsText();
text.setBold(re.getStartOffset(), re.getEndOffsetInclusive(), true);
}
});
}
18. Set document default font
function setDefaultFont_(fontName) {
getBody_().editAsText().setFontFamily(fontName || 'Arial');
}
19. Set font size of whole document
function setAllFontSize_(size) {
getBody_().editAsText().setFontSize(size || 11);
}
20. Highlight all headings
function highlightHeadings_(color) {
const body = getBody_();
body.getParagraphs().forEach(p => {
if (p.getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
p.setBackgroundColor(color || '#FFF3CD');
}
});
}
🔍 Section 5 – Find & Replace
21. Simple find text
function findOccurrences_(search) {
const body = getBody_();
const found = body.findText(search);
return found;
}
22. Find all occurrences and log
function findAllAndLog_(search) {
const body = getBody_();
let result = body.findText(search);
while (result) {
console.log('Found at', result.getStartOffset());
result = body.findText(search, result);
}
}
23. Replace first occurrence
function replaceFirst_(search, replacement) {
const body = getBody_();
const result = body.findText(search);
if (result) {
const text = result.getElement().asText();
text.replaceText(search, replacement);
}
}
24. Replace all occurrences
function replaceAll_(search, replacement) {
const body = getBody_();
body.replaceText(search, replacement);
}
25. Case-insensitive replace all
function replaceAllCaseInsensitive_(search, replacement) {
const body = getBody_();
const pattern = new RegExp(search, 'gi');
body.replaceText(pattern, replacement);
}
📑 Section 6 – Paragraphs & Structure
26. Count paragraphs
function countParagraphs_() {
return getBody_().getParagraphs().length;
}
27. Get all paragraph texts
function getAllParagraphTexts_() {
return getBody_().getParagraphs().map(p => p.getText());
}
28. Delete empty paragraphs
function removeEmptyParagraphs_() {
const body = getBody_();
const paras = body.getParagraphs();
for (let i = paras.length - 1; i >= 0; i--) {
if (!paras[i].getText().trim()) {
body.removeChild(paras[i]);
}
}
}
29. Convert all text to normal (remove headings)
function resetAllHeadingsToNormal_() {
getBody_().getParagraphs().forEach(p => {
p.setHeading(DocumentApp.ParagraphHeading.NORMAL);
});
}
30. Turn each line into a bulleted list
function convertToBulletedList_() {
const body = getBody_();
const paras = body.getParagraphs();
paras.forEach(p => p.setBullet(true));
}
31. Remove bullets from all paragraphs
function removeAllBullets_() {
getBody_().getParagraphs().forEach(p => p.removeFromList());
}
32. Indent all paragraphs by one level
function indentAll_() {
getBody_().getParagraphs().forEach(p => p.indentStart(36)); // 36pt approx
}
33. Set line spacing for all paragraphs
function setLineSpacing_(spacing) {
getBody_().getParagraphs().forEach(p => p.setLineSpacing(spacing || 1.15));
}
34. Add page break after each heading
function pageBreakAfterHeadings_() {
const body = getBody_();
const paras = body.getParagraphs();
for (let i = paras.length - 1; i >= 0; i--) {
if (paras[i].getHeading() !== DocumentApp.ParagraphHeading.NORMAL) {
body.insertPageBreak(i + 1);
}
}
}
35. Insert page break at cursor
function insertPageBreakAtCursor_() {
const doc = getActiveDoc_();
const cursor = doc.getCursor();
if (!cursor) return;
const el = cursor.insertPageBreak();
}
📚 Section 7 – Headings & Table of Contents
36. Set first paragraph as Title
function setFirstAsTitle_() {
const body = getBody_();
const first = body.getParagraphs()[0];
first.setHeading(DocumentApp.ParagraphHeading.TITLE);
}
37. Promote all headings one level (H3→H2, etc.)
function promoteHeadings_() {
const body = getBody_();
body.getParagraphs().forEach(p => {
const h = p.getHeading();
if (h === DocumentApp.ParagraphHeading.HEADING2) {
p.setHeading(DocumentApp.ParagraphHeading.HEADING1);
} else if (h === DocumentApp.ParagraphHeading.HEADING3) {
p.setHeading(DocumentApp.ParagraphHeading.HEADING2);
}
});
}
38. Demote headings one level
function demoteHeadings_() {
const body = getBody_();
body.getParagraphs().forEach(p => {
const h = p.getHeading();
if (h === DocumentApp.ParagraphHeading.HEADING1) {
p.setHeading(DocumentApp.ParagraphHeading.HEADING2);
} else if (h === DocumentApp.ParagraphHeading.HEADING2) {
p.setHeading(DocumentApp.ParagraphHeading.HEADING3);
}
});
}
39. Add a table of contents at top
function insertTOC_() {
const body = getBody_();
body.insertParagraph(0, 'Table of Contents')
.setHeading(DocumentApp.ParagraphHeading.HEADING1);
body.insertTable(1, [[DocumentApp.ElementType.TABLE_OF_CONTENTS]]);
}
40. Remove all table of contents elements
function removeAllTOC_() {
const body = getBody_();
const numChildren = body.getNumChildren();
for (let i = numChildren - 1; i >= 0; i--) {
const child = body.getChild(i);
if (child.getType() === DocumentApp.ElementType.TABLE_OF_CONTENTS) {
body.removeChild(child);
}
}
}
📝 Section 8 – Lists
41. Turn selection into a bulleted list
function selectionToBullets_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const p = re.getElement().getParent().asParagraph();
p.setBullet(true);
});
}
42. Turn selection into a numbered list
function selectionToNumberedList_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const p = re.getElement().getParent().asParagraph();
p.setGlyphType(DocumentApp.GlyphType.NUMBER);
});
}
43. Remove list formatting from selection
function clearListFromSelection_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const p = re.getElement().getParent().asParagraph();
p.removeFromList();
});
}
44. Increase indent level for list items
function indentListItems_() {
getBody_().getListItems().forEach(li => li.indentStart(36));
}
45. Decrease indent level for list items
function outdentListItems_() {
getBody_().getListItems().forEach(li => li.indentStart(0));
}
🖼️ Section 9 – Images
46. Insert image from URL at end
function insertImageFromUrl_(url) {
const blob = UrlFetchApp.fetch(url).getBlob();
getBody_().appendImage(blob);
}
47. Insert image at cursor
function insertImageAtCursor_(url) {
const doc = getActiveDoc_();
const cursor = doc.getCursor();
if (!cursor) return;
const blob = UrlFetchApp.fetch(url).getBlob();
cursor.insertInlineImage(blob);
}
48. Resize all images (width)
function resizeAllImages_(width) {
const body = getBody_();
const imgs = body.getImages();
imgs.forEach(img => img.setWidth(width || 300));
}
49. Add caption under each image
function addImageCaptions_(prefix) {
const body = getBody_();
const imgs = body.getImages();
imgs.forEach((img, index) => {
const parent = img.getParent().getParent();
const caption = body.insertParagraph(
body.getChildIndex(parent) + 1,
(prefix || 'Figure') + ' ' + (index + 1)
);
caption.setItalic(true);
});
}
50. Count images in document
function countImages_() {
return getBody_().getImages().length;
}
📊 Section 10 – Tables
51. Insert a simple 3×3 table at cursor or end
function insertSimpleTable_() {
const body = getBody_();
const table = body.appendTable();
for (let r = 0; r < 3; r++) {
const row = table.appendTableRow();
for (let c = 0; c < 3; c++) {
row.appendTableCell(`R${r+1}C${c+1}`);
}
}
}
52. Insert table from 2D array
function insertTableFromArray_(data) {
const body = getBody_();
const table = body.appendTable();
data.forEach(rowData => {
const row = table.appendTableRow();
rowData.forEach(cell => row.appendTableCell(String(cell)));
});
}
53. Style table header row
function styleTableHeader_() {
const body = getBody_();
body.getTables().forEach(table => {
const headerRow = table.getRow(0);
for (let i = 0; i < headerRow.getNumCells(); i++) {
const cell = headerRow.getCell(i);
cell.setBackgroundColor('#F3F4F6');
cell.editAsText().setBold(true);
}
});
}
54. Auto-size all table columns (simple approach)
function clearTableCellAttributes_() {
getBody_().getTables().forEach(table => {
for (let r = 0; r < table.getNumRows(); r++) {
const row = table.getRow(r);
for (let c = 0; c < row.getNumCells(); c++) {
row.getCell(c).clear();
}
}
});
}
(Docs doesn’t give fine-grained column width control via DocumentApp, but clearing attributes often lets Docs auto-size.)
55. Add border color to all tables
function setTableBorderColor_(color) {
getBody_().getTables().forEach(table => {
for (let r = 0; r < table.getNumRows(); r++) {
const row = table.getRow(r);
for (let c = 0; c < row.getNumCells(); c++) {
row.getCell(c).setBorderColorTop(color || '#000000')
.setBorderColorBottom(color || '#000000')
.setBorderColorLeft(color || '#000000')
.setBorderColorRight(color || '#000000');
}
}
});
}
📎 Section 11 – Links & Bookmarks
56. Add hyperlink to selected text
function addLinkToSelection_(url) {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const el = re.getElement();
if (el.editAsText) {
el.editAsText().setLinkUrl(re.getStartOffset(), re.getEndOffsetInclusive(), url);
}
});
}
57. Clear all links in document
function clearAllLinks_() {
const text = getBody_().editAsText();
text.setLinkUrl(null);
}
58. Insert bookmark at cursor
function insertBookmarkAtCursor_() {
const doc = getActiveDoc_();
const cursor = doc.getCursor();
if (!cursor) return;
doc.addBookmark(cursor);
}
59. List all bookmarks
function listBookmarks_() {
const doc = getActiveDoc_();
const bookmarks = doc.getBookmarks();
bookmarks.forEach(b => console.log(b.getId(), b.getPosition().getElement().getText()));
}
60. Remove all bookmarks
function removeAllBookmarks_() {
const doc = getActiveDoc_();
doc.getBookmarks().forEach(b => doc.removeBookmark(b));
}
📄 Section 12 – Headers, Footers & Page Setup
61. Ensure header exists
function ensureHeader_() {
const body = getBody_();
if (!body.getHeader()) {
body.setHeader(body.insertParagraph(0, ''));
}
}
62. Set header text
function setHeaderText_(text) {
const body = getBody_();
let header = body.getHeader();
if (!header) {
body.setHeader(body.insertParagraph(0, ''));
header = body.getHeader();
}
header.clear();
header.appendParagraph(text);
}
63. Set footer text
function setFooterText_(text) {
const body = getBody_();
let footer = body.getFooter();
if (!footer) {
body.setFooter(body.appendParagraph(''));
footer = body.getFooter();
}
footer.clear();
footer.appendParagraph(text);
}
64. Set page orientation to landscape
function setLandscape_() {
getBody_().setPageOrientation(DocumentApp.PageOrientation.LANDSCAPE);
}
65. Set page orientation to portrait
function setPortrait_() {
getBody_().setPageOrientation(DocumentApp.PageOrientation.PORTRAIT);
}
66. Insert page number placeholder in footer (simple text)
function insertPageNumberPlaceholder_() {
setFooterText_('Page {{pageNumber}}');
}
(Docs doesn’t support dynamic page numbers via DocumentApp, but this can act as a placeholder for templates.)
🧩 Section 13 – Selections & Cursors
67. Log current cursor position
function logCursorPosition_() {
const doc = getActiveDoc_();
const cursor = doc.getCursor();
if (!cursor) {
console.log('No cursor');
return;
}
console.log(cursor.getElement().getText());
}
68. Wrap selection in quotation marks
function quoteSelection_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const el = re.getElement();
if (el.editAsText) {
const text = el.editAsText();
const s = re.getStartOffset();
const e = re.getEndOffsetInclusive();
const selected = text.getText().substring(s, e + 1);
text.deleteText(s, e);
text.insertText(s, '"' + selected + '"');
}
});
}
69. Uppercase selection
function upperCaseSelection_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const el = re.getElement();
if (el.editAsText) {
const text = el.editAsText();
const s = re.getStartOffset();
const e = re.getEndOffsetInclusive();
const selected = text.getText().substring(s, e + 1);
text.deleteText(s, e);
text.insertText(s, selected.toUpperCase());
}
});
}
70. Lowercase selection
function lowerCaseSelection_() {
const doc = getActiveDoc_();
const sel = doc.getSelection();
if (!sel) return;
sel.getRangeElements().forEach(re => {
const el = re.getElement();
if (el.editAsText) {
const text = el.editAsText();
const s = re.getStartOffset();
const e = re.getEndOffsetInclusive();
const selected = text.getText().substring(s, e + 1);
text.deleteText(s, e);
text.insertText(s, selected.toLowerCase());
}
});
}
🔄 Section 14 – Template Placeholders
71. Replace simple placeholders like {{NAME}}
function replacePlaceholder_(key, value) {
const body = getBody_();
body.replaceText('{{' + key + '}}', value);
}
72. Fill multiple placeholders from an object
function fillTemplate_(data) {
const body = getBody_();
Object.keys(data).forEach(key => {
body.replaceText('{{' + key + '}}', String(data[key]));
});
}
73. Clear all placeholders ({{…}})
function clearAllPlaceholders_() {
const body = getBody_();
body.replaceText(/\{\{.*?\}\}/g, '');
}
74. Highlight all placeholders
function highlightPlaceholders_(color) {
const body = getBody_();
let result = body.findText(/\{\{.*?\}\}/);
while (result) {
const text = result.getElement().asText();
const s = result.getStartOffset();
const e = result.getEndOffsetInclusive();
text.setBackgroundColor(s, e, color || '#FFF3CD');
result = body.findText(/\{\{.*?\}\}/, result);
}
}
75. Generate a document from template ID
function createFromTemplate_(templateId, newName, data) {
const file = DriveApp.getFileById(templateId);
const copy = file.makeCopy(newName || (file.getName() + ' Filled'));
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
Object.keys(data || {}).forEach(key => {
body.replaceText('{{' + key + '}}', String(data[key]));
});
doc.saveAndClose();
return copy;
}
📂 Section 15 – Drive & Sharing Helpers
76. Get doc file object
function getDocFile_() {
const doc = getActiveDoc_();
return DriveApp.getFileById(doc.getId());
}
77. Move doc to a folder by ID
function moveDocToFolder_(folderId) {
const file = getDocFile_();
const folder = DriveApp.getFolderById(folderId);
const parents = file.getParents();
while (parents.hasNext()) {
parents.next().removeFile(file);
}
folder.addFile(file);
}
78. Share doc with a user (viewer)
function shareDocView_(email) {
getDocFile_().addViewer(email);
}
79. Share doc with a user (editor)
function shareDocEdit_(email) {
getDocFile_().addEditor(email);
}
80. Remove a user’s access
function removeUserAccess_(email) {
const file = getDocFile_();
file.removeEditor(email);
file.removeViewer(email);
}
🧹 Section 16 – Cleanup & Formatting Utilities
81. Trim extra spaces in document
function trimSpaces_() {
const text = getBody_().editAsText();
const cleaned = text.getText().replace(/[ \t]+/g, ' ');
text.setText(cleaned);
}
82. Remove double blank lines
function removeDoubleBlankLines_() {
const text = getBody_().editAsText();
const cleaned = text.getText().replace(/\n{3,}/g, '\n\n');
text.setText(cleaned);
}
83. Normalize quotes (“smart” to plain)
function normalizeQuotes_() {
const text = getBody_().editAsText();
const t = text.getText()
.replace(/[“”]/g, '"')
.replace(/[‘’]/g, "'");
text.setText(t);
}
84. Remove trailing spaces at line ends
function removeTrailingSpaces_() {
const text = getBody_().editAsText();
const t = text.getText().replace(/[ \t]+$/gm, '');
text.setText(t);
}
85. Convert all tabs to 4 spaces
function tabsToSpaces_() {
const text = getBody_().editAsText();
text.setText(text.getText().replace(/\t/g, ' '));
}
🗂️ Section 17 – Export & Integration
86. Get document as PDF blob
function getPdfBlob_() {
const file = getDocFile_();
return file.getAs('application/pdf');
}
87. Email document as PDF
function emailDocAsPdf_(email, subject, body) {
const pdf = getPdfBlob_();
MailApp.sendEmail({
to: email,
subject: subject || 'Document PDF',
body: body || 'Please find attached.',
attachments: [pdf]
});
}
88. Get document plain text
function getDocPlainText_() {
return getBody_().getText();
}
89. Export document as plain text to Drive
function saveDocAsTxt_() {
const file = getDocFile_();
const name = file.getName() + '.txt';
DriveApp.createFile(name, getBody_().getText(), MimeType.PLAIN_TEXT);
}
90. Export doc as HTML (simple)
function saveDocAsHtml_() {
const file = getDocFile_();
const blob = file.getBlob();
DriveApp.createFile(file.getName() + '.html', blob.getDataAsString(), MimeType.HTML);
}
(This is a rough export; for full fidelity use Drive API exports.)
🧰 Section 18 – Menus & UI
91. Add a custom menu
function onOpen() {
DocumentApp.getUi()
.createMenu('Doc Tools')
.addItem('Clean up double blank lines', 'removeDoubleBlankLines_')
.addItem('Trim spaces', 'trimSpaces_')
.addSeparator()
.addItem('Insert TOC', 'insertTOC_')
.addToUi();
}
92. Show a basic alert
function showAlert_(msg) {
DocumentApp.getUi().alert(msg || 'Done!');
}
93. Prompt user for placeholder value
function fillSinglePlaceholderWithPrompt_() {
const ui = DocumentApp.getUi();
const resp = ui.prompt('Placeholder', 'Enter value for {{NAME}}', ui.ButtonSet.OK_CANCEL);
if (resp.getSelectedButton() === ui.Button.OK) {
replacePlaceholder_('NAME', resp.getResponseText());
ui.alert('Updated {{NAME}}');
}
}
94. Confirmation before running clean-up
function confirmAndClean_() {
const ui = DocumentApp.getUi();
const resp = ui.alert('Cleanup', 'Run cleanup on this document?', ui.ButtonSet.YES_NO);
if (resp === ui.Button.YES) {
trimSpaces_();
removeDoubleBlankLines_();
ui.alert('Cleanup complete');
}
}
95. Simple sidebar with instructions
function showSidebar_() {
const html = HtmlService.createHtmlOutput('<p>Welcome to Doc Tools!</p>')
.setTitle('Doc Tools');
DocumentApp.getUi().showSidebar(html);
}
⚙️ Section 19 – Triggers & Logging
96. Log when doc is opened (via installable trigger)
function onOpenLogger_(e) {
console.log('Document opened at', new Date());
}
(Install as an installable trigger tied to the document.)
97. Auto-clean on edit (for specific workflows)
function onEditDoc_(e) {
// Not a simple Docs trigger – you’d use onOpen + manual call
trimSpaces_();
}
(Docs doesn’t support a simple onEdit like Sheets; this is more of a pattern placeholder.)
98. Timestamp footer update
function updateFooterTimestamp_() {
const now = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd HH:mm');
setFooterText_('Last updated: ' + now);
}
99. Save doc then log size
function saveAndLogSize_() {
const doc = getActiveDoc_();
doc.saveAndClose();
const file = getDocFile_();
console.log('File size (bytes):', file.getSize());
}
100. Master “daily cleanup” function
function dailyDocCleanup_() {
trimSpaces_();
removeDoubleBlankLines_();
removeTrailingSpaces_();
normalizeQuotes_();
updateFooterTimestamp_();
showAlert_('Daily cleanup complete!');
}
✅ How to Use These Functions
- In your Google Doc, go to Extensions → Apps Script.
- Create a file named
docsHelpers.gs. - Paste in the functions you want from this post.
- Save, then reload the Doc to trigger
onOpen()menus. - Run functions from:
- The Run menu in the editor, or
- Your new custom menu in the Doc (after
onOpenruns).
You now have a toolbox of 100 common Google Docs Apps Script helpers you can mix, match, and extend for your own workflows, templates, and automations.
