Insert chapter divider slides from an outline
Add section-break slides between chapters in a Northwind deck.
Published Dec 21, 2025
Long Northwind decks — the annual review, the all-hands, the internal training sequence — all benefit from chapter dividers. A title-only slide between sections gives the audience a moment to catch up, signals a topic change, and saves the presenter from awkwardly saying “right, moving on”. The trouble is that decks shift as they are written, and inserting dividers by hand means recalculating which slide number each one belongs at every time.
This script takes an outline — a list of { beforeSlide, title } objects in
the order they appear — and inserts a title-layout divider slide before each
target slide. The clever bit is the running offset: each insertion pushes
later slides down by one, so the script keeps a counter and adjusts the index
as it goes. You write the outline once, and a deck that already has 6 chapter
breaks becomes one with 6 fresh divider slides in the right places.
What you’ll need
- A Google Slides deck to add dividers to.
- An outline of chapters: each item gives the slide the divider should appear before (1-based, against the original deck order) and the title to show on the divider.
- Nothing else — no external services or API keys.
The script
// The layout to use for divider slides. TITLE is the simplest — one big
// centred heading. Switch to SECTION_HEADER if your template defines it.
const DIVIDER_LAYOUT = SlidesApp.PredefinedLayout.TITLE;
/**
* Inserts a divider slide before each entry in `chapters`. The chapter list
* is in original-deck coordinates — the script applies a running offset so
* later inserts land where you expect.
*
* @param {string} deckId The Slides file ID.
* @param {Array<{beforeSlide: number, title: string}>} chapters
* Chapter breaks in deck order (1-based slide numbers).
*/
function insertDividers(deckId, chapters) {
if (!deckId) throw new Error('insertDividers needs a deckId.');
if (!Array.isArray(chapters) || !chapters.length) {
throw new Error('insertDividers needs at least one chapter.');
}
const deck = SlidesApp.openById(deckId);
let inserted = 0;
// 1. Sort by target position so the running-offset trick works. If the
// caller already passed them in order this is a no-op.
const ordered = [...chapters].sort((a, b) => a.beforeSlide - b.beforeSlide);
for (const ch of ordered) {
if (!ch.title) {
Logger.log(`Skipping chapter at slide ${ch.beforeSlide} — no title.`);
continue;
}
// 2. Each previous insert pushed later slides down by one, so add the
// running offset to convert the original index to the live index.
// insertSlide is 0-based, but beforeSlide is 1-based.
const liveIndex = (ch.beforeSlide - 1) + inserted;
const divider = deck.insertSlide(liveIndex, DIVIDER_LAYOUT);
// 3. Set the title on the first placeholder. TITLE layout has exactly
// one — if you switch to SECTION_HEADER, this still works.
const placeholders = divider.getPlaceholders();
if (placeholders.length) {
placeholders[0].asShape().getText().setText(ch.title);
} else {
Logger.log(`Inserted divider at ${ch.beforeSlide} but layout had no placeholder.`);
}
inserted++;
}
Logger.log(`Inserted ${inserted} divider slides into deck ${deckId}.`);
}
/**
* Convenience wrapper with a sample outline. Replace the deck ID and
* chapters with your own.
*/
function run() {
insertDividers('1abcDeckId', [
{ beforeSlide: 1, title: 'Overview' },
{ beforeSlide: 6, title: 'Case studies' },
{ beforeSlide: 12, title: 'Next steps' },
]);
}
How it works
insertDividersvalidates its inputs first — running the script with an empty array would silently do nothing and look like a bug.- It sorts the chapters by
beforeSlidebefore iterating. The running-offset technique only works when later inserts come after earlier ones, so the sort makes the function robust to callers that pass chapters out of order. - For each chapter it skips entries without a title — a blank divider is never what you want.
- The index math is the heart of the script.
beforeSlideis in original-deck coordinates (1-based), but every previous insert has pushed later slides down by one. Subtracting one (for 0-based) and addinginserted(the running offset) gives the right index forinsertSlide. deck.insertSlide(index, layout)adds a new slide on the given layout at that position and returns the new slide. TheTITLElayout has a single title placeholder, which the script fills in with the chapter title.- It logs the total at the end so you can confirm the count matches the outline before opening the deck.
Example run
Say 1abcDeckId is a 14-slide deck and the outline is:
| beforeSlide | title |
|---|---|
| 1 | Overview |
| 6 | Case studies |
| 12 | Next steps |
After a run, the deck has grown to 17 slides:
| Position | Slide |
|---|---|
| 1 | Overview (new divider) |
| 2 | Original slide 1 |
| … | … |
| 6 | Case studies (new divider) |
| 7 | Original slide 6 |
| … | … |
| 14 | Next steps (new divider) |
| 15 | Original slide 12 |
| 16-17 | Original slides 13-14 |
The log reads Inserted 3 divider slides into deck 1abcDeckId.
Run it
This is an on-demand job — run it whenever the outline is final:
- Write your outline in the
runwrapper or pass it from another script. - In the Apps Script editor, select
runand click Run. - Approve the authorisation prompt the first time.
- Open the deck and check that the dividers landed in the expected places.
For a recurring deck (a weekly all-hands, say), keep the outline in a spreadsheet and have the script read it — see the populate-speaker-notes recipe for the sheet-reading pattern.
Watch out for
- The chapter indexes are in original-deck coordinates. If you re-run the script on a deck that already has dividers, every new divider is inserted in addition to the old ones — there is no idempotency check.
- The
TITLElayout has exactly one placeholder. If you swap it for a custom layout with no title placeholder, the script logs a warning but still inserts the slide, leaving the title blank. - Slide numbering in the outline must match what humans see in the deck. Hidden or skipped slides still count — Slides numbers by position, not by visibility.
- The script does not style the divider. It inherits the master layout’s typography. If you want bigger text or a brand colour, adjust the master layout itself rather than per-divider in code.
Related
Extract all deck text into a sheet
Pull text out of every slide for review, translation, or copy-editing.
Updated Jan 4, 2026
Generate sales-enablement decks per segment
Tailor Northwind's messaging slides by audience segment — fintech, healthcare, retail.
Updated Dec 28, 2025
Build a deck accessibility checker
Flag missing alt text, low contrast, and tiny fonts across a Northwind deck.
Updated Dec 14, 2025
Drive menu and price-list signage from a Sheet
Generate display slides for a Northwind venue — menus or price lists driven by a Sheet.
Updated Dec 7, 2025
Build a Slides-based countdown timer
Embed a live countdown timer in a Northwind deck — refreshed via a scheduled script.
Updated Nov 30, 2025