§ 02 · Recipes

Common scenarios, type-checked snippets.

Every snippet below lives under site/src/lib/examples/ and is type-checked against the live @office-kit/pptx surface on every build — an API rename breaks this page before anything ships. Need a one-line lookup? See the Cheatsheet. Looking up a specific function? Jump to the API reference.

Basics

01

# Open a presentation and walk every slide

Load an existing .pptx, iterate slides, and read title + text content.

where: site/src/lib/examples/recipes/open-and-iterate.ts

A1 site/src/lib/examples/recipes/open-and-iterate.ts .ts
// Load a presentation, walk every slide, print titles and text length.

import { readFile } from 'node:fs/promises';
import {
  getSlideText,
  getSlideTextLength,
  getSlideTitle,
  getSlides,
  loadPresentation,
} from '@office-kit/pptx';

const pres = await loadPresentation(await readFile('input.pptx'));
for (const slide of getSlides(pres)) {
  console.log(`${getSlideTitle(slide) ?? '(no title)'} — ${getSlideTextLength(slide)} chars`);
  console.log(getSlideText(slide));
}
  • getSlideTitle returns the title placeholder text or null if the slide has no title placeholder.
  • getSlideText concatenates every text body on the slide; getSlideOutline gives a structured outline if you need paragraph hierarchy.
02

# Token-based template fill

The canonical L2 workflow: open a designer-authored template, swap `{{tokens}}`, save.

where: site/src/lib/examples/template-fill.ts

B1 site/src/lib/examples/template-fill.ts .ts
// Open a template .pptx, replace `{{tokens}}` everywhere, save it back.
//
// This file is imported as ?raw into the docs site so the snippet shown
// to readers is exactly what svelte-check / tsc compiled — if an API
// rename breaks this import, the docs build fails before deploy.

import { readFile, writeFile } from 'node:fs/promises';
import { loadPresentation, replaceTokensInPresentation, savePresentation } from '@office-kit/pptx';

const pres = await loadPresentation(await readFile('template.pptx'));
replaceTokensInPresentation(pres, {
  name: 'Yamashita',
  event: 'Re:Invent',
  date: '2026-12-01',
});
await writeFile('out.pptx', await savePresentation(pres));
  • Tokens are exact-match: {{name}} replaces only the literal `{{name}}` string in any text run.
  • replaceTokensInPresentation also walks speaker notes; use replaceTokensInSlide for a single slide.
03

# Build a deck on a blank template

Start from a tiny blank.pptx, add slides on its layouts, and save.

where: site/src/lib/examples/recipes/build-from-scratch.ts

C1 site/src/lib/examples/recipes/build-from-scratch.ts .ts
// Build a deck on top of a blank template: add a title slide and a content
// slide, then save.

import { readFile, writeFile } from 'node:fs/promises';
import {
  addSlide,
  addSlideTextBox,
  findSlideLayout,
  findSlidePlaceholder,
  inches,
  loadPresentation,
  savePresentation,
  setShapeText,
} from '@office-kit/pptx';

const pres = await loadPresentation(await readFile('blank.pptx'));

const titleLayout = findSlideLayout(pres, 'Title Slide');
if (titleLayout) {
  const cover = addSlide(pres, { layout: titleLayout });
  const t = findSlidePlaceholder(cover, 'ctrTitle') ?? findSlidePlaceholder(cover, 'title');
  if (t) setShapeText(t, '@office-kit/pptx demo');
  const sub = findSlidePlaceholder(cover, 'subTitle');
  if (sub) setShapeText(sub, 'an OOXML library for TypeScript');
}

const blank = findSlideLayout(pres, 'Blank') ?? titleLayout;
if (blank) {
  const body = addSlide(pres, { layout: blank });
  addSlideTextBox(body, {
    x: inches(1),
    y: inches(1),
    w: inches(8),
    h: inches(1),
    text: 'Free-form text box',
  });
}

await writeFile('out.pptx', await savePresentation(pres));
04

# Brand a deck’s color scheme + fonts

setPresentationTheme / setPresentationFonts patch the theme’s color and font scheme — no template required.

where: site/src/lib/examples/recipes/theme-branding.ts

D1 site/src/lib/examples/recipes/theme-branding.ts .ts
// Brand a from-scratch deck's color scheme and typography — no template
// required. setPresentationTheme / setPresentationFonts patch only the
// slots you name; every other theme slot keeps its default.

import {
  createPresentation,
  setPresentationFonts,
  setPresentationTheme,
  type PresentationData,
} from '@office-kit/pptx';

const pres: PresentationData = createPresentation();

setPresentationTheme(pres, {
  name: 'Consulting Navy',
  dark1: '#0B1F3A',
  accent1: '#00A9E0',
  accent2: '#FDB913',
  accent3: '#6E7B8B',
});

setPresentationFonts(pres, {
  majorLatin: 'Georgia',
  minorLatin: 'Calibri',
});

// Every shape added from here on inherits the new scheme via the
// scheme-color tokens (accent1, tx1, ...) it was already using.
  • Only the slots you pass are overwritten; every other color/typeface keeps its default.
  • Colors are #RRGGBB strings; every theme slot is always a plain srgbClr, never a scheme-color reference.
05

# Direct fs helpers (Node)

loadPresentationFile / savePresentationToFile skip the manual fs glue.

where: site/src/lib/examples/node-fs.ts

E1 site/src/lib/examples/node-fs.ts .ts
// One-shot read + save direct from / to disk via the @office-kit/pptx/node
// helpers, no manual fs glue needed.

import { loadPresentationFile, savePresentationToFile } from '@office-kit/pptx/node';

const pres = await loadPresentationFile('input.pptx');
// ...mutate pres...
await savePresentationToFile(pres, 'output.pptx');

Authoring shapes

06

# Per-run text formatting

Address text by (paragraph, run) indices and apply font, size, color, bold / italic / underline.

where: site/src/lib/examples/recipes/text-formatting.ts

F1 site/src/lib/examples/recipes/text-formatting.ts .ts
// Per-run formatting: bold, size, color, font on a single paragraph.
//
// Index a shape's text by (paragraphIndex, runIndex). Each \n in the
// `text:` argument starts a new paragraph; the first run of each is
// index 0.

import {
  addSlideTextBox,
  inches,
  setParagraphAlignment,
  setShapeRunFormat,
  type SlideData,
} from '@office-kit/pptx';

declare const slide: SlideData;

const box = addSlideTextBox(slide, {
  x: inches(0.7),
  y: inches(1.5),
  w: inches(9),
  h: inches(2.5),
  text: 'Default text\nBold red 24pt Calibri\nItalic underline 18pt',
});
setShapeRunFormat(box, 1, 0, {
  bold: true,
  size: 24,
  color: '#C00000',
  font: 'Calibri',
});
setShapeRunFormat(box, 2, 0, {
  italic: true,
  underline: true,
  size: 18,
  color: '#1F4E79',
});
setParagraphAlignment(box, 2, 'ctr');
  • TextFormat.size is in points (number), not EMU. Color accepts #RRGGBB, RRGGBB, or a scheme token like "accent1".
  • setShapeTextFormat (no index) applies the same format to every run in the shape.
07

# Preset shapes (180+ presets)

addSlideShape ships every ECMA-376 preset geometry — pass the token as `preset`.

where: site/src/lib/examples/recipes/preset-shapes.ts

G1 site/src/lib/examples/recipes/preset-shapes.ts .ts
// Drop in a few preset shapes from the 180+ DrawingML preset library.

import { addSlideShape, inches, type SlideData } from '@office-kit/pptx';

declare const slide: SlideData;

addSlideShape(slide, {
  preset: 'star5',
  x: inches(1),
  y: inches(2),
  w: inches(2),
  h: inches(2),
  text: '★',
});
addSlideShape(slide, {
  preset: 'rightArrow',
  x: inches(4),
  y: inches(2.4),
  w: inches(2.5),
  h: inches(1.2),
  text: 'next',
});
addSlideShape(slide, {
  preset: 'roundRect',
  x: inches(7),
  y: inches(2),
  w: inches(2.5),
  h: inches(2),
  text: 'callout',
});
  • Use PresetShape from the public types for autocompletion. Unknown strings pass through to <a:prstGeom prst="..."/> verbatim.
08

# Fills, shadows, glows

Solid / gradient / pattern fills plus the two effect helpers.

where: site/src/lib/examples/recipes/fills-and-effects.ts

H1 site/src/lib/examples/recipes/fills-and-effects.ts .ts
// Solid / gradient / pattern fills + a drop shadow and an outer glow.

import {
  addSlideShape,
  inches,
  pt,
  setShapeFill,
  setShapeGlow,
  setShapeGradientFill,
  setShapePatternFill,
  setShapeShadow,
  type SlideData,
} from '@office-kit/pptx';

declare const slide: SlideData;

const solid = addSlideShape(slide, {
  preset: 'rect',
  x: inches(0.7),
  y: inches(1),
  w: inches(2.5),
  h: inches(2),
  text: 'Solid',
});
setShapeFill(solid, '#C00000');
setShapeShadow(solid, {
  blurEmu: pt(8),
  offsetEmu: pt(4),
  angleDeg: 45,
  color: '#000000',
  opacity: 0.5,
});

const grad = addSlideShape(slide, {
  preset: 'rect',
  x: inches(3.5),
  y: inches(1),
  w: inches(2.5),
  h: inches(2),
  text: 'Gradient',
});
setShapeGradientFill(grad, {
  stops: [
    { offset: 0, color: '#FFD966' },
    { offset: 1, color: '#C00000' },
  ],
  angleDeg: 45,
});

const pat = addSlideShape(slide, {
  preset: 'rect',
  x: inches(6.3),
  y: inches(1),
  w: inches(2.5),
  h: inches(2),
  text: 'Pattern',
});
setShapePatternFill(pat, { preset: 'pct50', foreground: '#1F4E79', background: '#FFFFFF' });
setShapeGlow(pat, { radiusEmu: pt(12), color: '#2E75B6' });
09

# Group shapes into one component

Compose a rectangle + label into a "KPI card"; move/resize the group as one unit.

where: site/src/lib/examples/recipes/group-shapes.ts

I1 site/src/lib/examples/recipes/group-shapes.ts .ts
// Compose a rectangle + label into a single "KPI card" component, then
// move/resize it as one unit — the members scale with it.

import {
  addSlideShape,
  addSlideTextBox,
  groupShapes,
  inches,
  setShapeFill,
  setShapePosition,
  setShapeSize,
  type SlideData,
} from '@office-kit/pptx';

declare const slide: SlideData;

const card = addSlideShape(slide, {
  preset: 'roundRect',
  x: inches(1),
  y: inches(1),
  w: inches(2.5),
  h: inches(1.2),
});
setShapeFill(card, '#0B1F3A');

const label = addSlideTextBox(slide, {
  x: inches(1.2),
  y: inches(1.3),
  w: inches(2.1),
  h: inches(0.6),
  text: 'Revenue +12%',
});

const kpiCard = groupShapes([card, label], { name: 'KPI Card' });

// Move + resize the group; the rectangle and label scale together.
setShapePosition(kpiCard, inches(5), inches(2));
setShapeSize(kpiCard, inches(3.75), inches(1.8));
  • groupShapes needs every member to have an explicit position/size — placeholders that inherit geometry from the layout can't be grouped directly.
  • ungroupShapes reverses it, rescaling each member if the group itself was moved/resized in between.

Tables, charts, images

10

# Add a table

A 2D array of cell strings, plus firstRow / bandRow flags for header styling.

where: site/src/lib/examples/recipes/add-table.ts

J1 site/src/lib/examples/recipes/add-table.ts .ts
// Add a 5×4 table with a header row and banded styling.

import { addSlideTable, inches, type SlideData } from '@office-kit/pptx';

declare const slide: SlideData;

addSlideTable(slide, {
  x: inches(0.7),
  y: inches(1.5),
  w: inches(9),
  h: inches(3),
  rows: [
    ['Quarter', 'Revenue', 'Cost', 'Margin'],
    ['Q1', '$1.2M', '$0.8M', '33%'],
    ['Q2', '$1.8M', '$0.9M', '50%'],
    ['Q3', '$2.4M', '$1.3M', '46%'],
    ['Q4', '$3.0M', '$1.6M', '47%'],
  ],
  firstRow: true,
  bandRow: true,
});
  • Per-cell control after the fact: getTableCell + setTableCellText / setTableCellFill / setTableCellAlignment.
  • insertTableRow / removeTableRow / insertTableColumn / removeTableColumn for structural changes.
11

# Add a chart

addSlideChart writes the chart XML, the drawing rels, and an embedded xlsx so "Edit data" works in PowerPoint.

where: site/src/lib/examples/recipes/add-chart.ts

K1 site/src/lib/examples/recipes/add-chart.ts .ts
// Column chart with two series. addSlideChart writes the chart XML, the
// drawing rels, and an embedded xlsx so PowerPoint's "Edit data" works.

import { addSlideChart, inches, type SlideData } from '@office-kit/pptx';

declare const slide: SlideData;

addSlideChart(slide, {
  x: inches(0.5),
  y: inches(1.5),
  w: inches(9),
  h: inches(4.5),
  spec: {
    kind: 'column',
    categories: ['Q1', 'Q2', 'Q3', 'Q4'],
    series: [
      { name: 'Revenue', values: [120, 180, 240, 300] },
      { name: 'Cost', values: [80, 90, 130, 160] },
    ],
    title: 'FY26',
  },
});
  • Chart kinds: bar, column, line, pie, doughnut, area. Each takes the same { categories, series } shape.
  • setChartSpec updates an existing chart in place — useful for re-rendering a template chart with new data.
12

# Insert an image

Format auto-detected from magic bytes. Reuse setShapeImage to swap an existing picture.

where: site/src/lib/examples/recipes/add-image.ts

L1 site/src/lib/examples/recipes/add-image.ts .ts
// Insert an image. setShapeImage / addSlideImage detect the format
// (PNG / JPEG / GIF / SVG / BMP / TIFF) from magic bytes.

import { readFile } from 'node:fs/promises';
import { addSlideImage, inches, type SlideData } from '@office-kit/pptx';

declare const slide: SlideData;

const logo = await readFile('logo.png');
addSlideImage(slide, logo, {
  x: inches(8),
  y: inches(0.4),
  w: inches(1.5),
  h: inches(1),
});
13

# Replace a template image in place

Find a placeholder picture by name and swap its bytes — geometry / crop preserved.

where: site/src/lib/examples/image-replace.ts

M1 site/src/lib/examples/image-replace.ts .ts
// Swap an image in a template, preserving its crop / transform / sizing.
// The format is auto-detected from the new bytes.

import { readFile, writeFile } from 'node:fs/promises';
import {
  getShapeKind,
  getShapeName,
  getSlideShapes,
  getSlides,
  loadPresentation,
  savePresentation,
  setShapeImage,
} from '@office-kit/pptx';

const pres = await loadPresentation(await readFile('template.pptx'));
const newLogo = await readFile('new-logo.png');

for (const slide of getSlides(pres)) {
  for (const shape of getSlideShapes(slide)) {
    if (getShapeKind(shape) === 'picture' && getShapeName(shape) === 'Logo') {
      setShapeImage(shape, newLogo);
    }
  }
}
await writeFile('out.pptx', await savePresentation(pres));

L4: notes, comments, transitions, animations

14

# Speaker notes + review comments

Notes live on the slide; comments are separate parts with author metadata and an EMU position.

where: site/src/lib/examples/recipes/notes-and-comments.ts

N1 site/src/lib/examples/recipes/notes-and-comments.ts .ts
// Add speaker notes and two review comments to a slide.

import { addSlideComment, cm, setSlideNotes, type SlideData } from '@office-kit/pptx';

declare const slide: SlideData;

setSlideNotes(slide, 'Open the comments pane to see the review thread.');

addSlideComment(slide, {
  author: { name: 'Reviewer A', initials: 'RA' },
  text: 'Tighten the headline.',
  position: { x: cm(2), y: cm(2) },
});
addSlideComment(slide, {
  author: { name: 'Reviewer B', initials: 'RB' },
  text: 'Numbers look strong.',
  position: { x: cm(10), y: cm(6) },
});
15

# Slide transitions + shape animations

Per-slide transition effect plus entrance / exit animations on individual shapes.

where: site/src/lib/examples/recipes/transitions-animations.ts

O1 site/src/lib/examples/recipes/transitions-animations.ts .ts
// Slide transitions + shape entrance / exit animations.

import {
  addSlideShape,
  inches,
  setShapeAnimation,
  setShapeFill,
  setSlideTransition,
  type SlideData,
} from '@office-kit/pptx';

declare const slide: SlideData;

setSlideTransition(slide, { effect: 'fade', speed: 'med' });

const a = addSlideShape(slide, {
  preset: 'roundRect',
  x: inches(1),
  y: inches(2),
  w: inches(3),
  h: inches(1.5),
  text: 'fadeIn',
});
setShapeFill(a, '#2E75B6');
setShapeAnimation(a, { effect: 'fadeIn', durationMs: 700 });

const b = addSlideShape(slide, {
  preset: 'roundRect',
  x: inches(5),
  y: inches(2),
  w: inches(3),
  h: inches(1.5),
  text: 'fadeOut',
});
setShapeFill(b, '#C00000');
setShapeAnimation(b, { effect: 'fadeOut', durationMs: 700 });
  • Animation effects: fadeIn, fadeOut, appear, disappear. Deeper timing-tree authoring is post-1.0.

Diagnostics

17

# Validate the package

validatePresentation returns invariant violations: dangling rels, missing parts, off-spec IDs.

where: site/src/lib/examples/recipes/validate.ts

Q1 site/src/lib/examples/recipes/validate.ts .ts
// Run the invariant checker before saving — useful in CI to catch
// dangling rels / missing parts / off-spec IDs.

import { type PresentationData, validatePresentation } from '@office-kit/pptx';

declare const pres: PresentationData;

const issues: ReturnType<typeof validatePresentation> = validatePresentation(pres);
for (const issue of issues) {
  console.error(`[${issue.severity}] ${issue.message}`);
}
if (issues.some((i) => i.severity === 'error')) {
  throw new Error(`@office-kit/pptx: ${issues.length} validation issue(s)`);
}
18

# Inspect raw OPC parts

listPackageParts + readPackagePart let you peek at the underlying XML without dropping to the internal class.

where: site/src/lib/examples/recipes/inspect-parts.ts

R1 site/src/lib/examples/recipes/inspect-parts.ts .ts
// Inspect the raw OPC parts in a package without dropping to the
// internal OpcPackage class.

import { type PresentationData, listPackageParts, readPackagePart } from '@office-kit/pptx';

declare const pres: PresentationData;

for (const part of listPackageParts(pres)) {
  console.log(`${part.name} (${part.contentType}, ${part.byteLength} bytes)`);
}

const themeBytes = readPackagePart(pres, '/ppt/theme/theme1.xml');
if (themeBytes) console.log(new TextDecoder().decode(themeBytes));

Browser

19

# Browser: load via fetch

loadPresentation accepts ArrayBuffer / Uint8Array / Blob — pipe a fetch Response straight in.

where: site/src/lib/examples/browser-fetch.ts

S1 site/src/lib/examples/browser-fetch.ts .ts
// Browser: fetch a .pptx and load it. loadPresentation accepts any of
// Uint8Array / ArrayBuffer / Blob — `await response.arrayBuffer()` is the
// most browser-portable form.

import { getSlides, getSlideTitle, loadPresentation } from '@office-kit/pptx';

const response = await fetch('/template.pptx');
const pres = await loadPresentation(await response.arrayBuffer());
for (const slide of getSlides(pres)) {
  console.log(getSlideTitle(slide));
}

Want to drive a deck end-to-end? Walk the Getting started page, then open the playground to inspect a real file.