Merge multiple docs into one in Google Docs
Source: Dev.to
Originally written for bulldo.gs — republished here with the canonical link pointing home. I want to programmatically combine several Google Docs into one file without losing tables or list formatting. // Merges all source docs into destDocId, in order. // Run from the Apps Script editor; no triggers needed. function mergeDocs() { var sourceIds = [ ‘DOC_ID_ONE’, ‘DOC_ID_TWO’, ‘DOC_ID_THREE’ ]; var dest = DocumentApp.openById(‘DEST_DOC_ID’).getBody(); for (var i = 0; i Apps Script from within that doc) so the project stays easy to find later. One quota note: DocumentApp.openById counts against the Apps Script read quota, which is 50 opens per execution for consumer accounts and higher for Workspace. For more than roughly 40 source docs, batch across multiple executions or use a time-driven trigger that processes a slice at a time. List items in Google Docs carry a list ID that ties them to a specific list group. When you copy a ListItem from one document into another, Apps Script assigns it a new list ID automatically, so items from different source docs will not accidentally merge into the same numbered list. That behavior is what you want. Where it surprises people: if a single source doc has two separate bullet lists separated by a paragraph, and that paragraph is a heading (which Apps Script still sees as PARAGRAPH), the heading copies correctly and the two lists stay separate. The problem only appears if you skip paragraph elements, which drops the visual boundary and makes two distinct lists appear to run together in the output. Copy everything in child order and this does not happen. Does this preserve bold, italic, and heading styles? Yes. The .copy() call on a paragraph or list item carries over all inline text attributes (bold, italic, underline, font size) and the paragraph style (Heading 1, Normal, etc.). What it does not carry over is named styles you defined via a custom Style; those stay tied to the source document’s stylesheet. Why does my table appear empty after the merge? You likely called appendTable with the raw Element object rather than casting it first with el.asTable(). The cast returns a proper Table instance whose .copy() includes all rows and cells. Skipping the cast passes an untyped element and the API creates an empty table shell. Can I merge docs from a shared drive I do not own? Yes, as long as the account running the script has at least Viewer access on each source doc. The OAuth scope https://www.googleapis.com/auth/documents covers cross-ownership reads. If a source doc is restricted and the script account lacks access, openById throws a GoogleJsonResponseException with a 403; fix it by sharing the source doc with the script-runner’s account. The script stops after a few docs with a ‘Service invoked too many times’ error. That is the Apps Script DocumentService quota cap. For consumer (free) Google accounts, the daily limit is 250 document opens. For Google Workspace accounts it is higher but still finite. Split your source IDs into batches of 20-30 and run the function multiple times, or set a time-driven trigger that processes one batch per minute. Want the plain-English version? Describe the automation at bulldo.gs and get working Apps Script back — free, no login.