Getting started

This page walks the canonical @office-kit/pptx workflows: fill a template, build a deck from scratch, edit images in place. Every snippet below is a real .ts file in this repo — svelte-check compiles them against the live @office-kit/pptx types on every build, so when this page renders, it’s already proven that the code typechecks.

Token-based template fill

The most common @office-kit/pptx use case: take a designer-authored template, swap {{tokens}} with your data, save. Formatting on the surrounding runs is preserved.

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));

loadPresentation accepts a Uint8Array, ArrayBuffer, or Blob — anything you can fetch or readFile. replaceTokensInPresentation walks every text run in every slide (and the speaker notes) replacing exact-match {{name}} tokens.

Build a deck from a blank template

For authoring from scratch you still need at least one master + layout — the simplest path is to ship a small blank.pptx alongside your app. Then add slides on its layouts and fill them in with the typed addSlide* helpers:

site/src/lib/examples/build-deck.ts .ts
// Build a deck from a blank template: add slides on a layout, drop in a
// text box, an image, and a chart, then save.

import { readFile, writeFile } from 'node:fs/promises';
import {
  addSlide,
  addSlideChart,
  addSlideImage,
  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) throw new Error('no Title Slide layout');

const cover = addSlide(pres, { layout: titleLayout });
const title = findSlidePlaceholder(cover, 'ctrTitle') ?? findSlidePlaceholder(cover, 'title');
if (title) setShapeText(title, 'Q3 review');

const blank = findSlideLayout(pres, 'Blank') ?? titleLayout;
const slide = addSlide(pres, { layout: blank });
addSlideTextBox(slide, {
  x: inches(0.7),
  y: inches(0.5),
  w: inches(9),
  h: inches(0.7),
  text: 'Numbers up and to the right',
});
addSlideChart(slide, {
  x: inches(0.7),
  y: inches(1.5),
  w: inches(8),
  h: inches(4.5),
  spec: {
    kind: 'column',
    categories: ['Q1', 'Q2', 'Q3', 'Q4'],
    series: [{ name: 'Revenue', values: [120, 180, 240, 300] }],
    title: 'FY26',
  },
});
addSlideImage(slide, await readFile('logo.png'), {
  x: inches(8),
  y: inches(0.4),
  w: inches(1.5),
  h: inches(1),
});

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

inches(n), cm(n), pt(n), and emu(n) all return a branded Emu number — internally @office-kit/pptx only sees EMUs, so unit confusion is caught at the type level.

Replace an image, preserve geometry

A common L2 (template-edit) workflow: a designer drops a placeholder image into the template, your code swaps in the real one without disturbing crop, position, or scaling.

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));

setShapeImage auto-detects the new image’s format (PNG / JPEG / GIF / SVG / BMP / TIFF) from magic bytes — pass options.format to override.

Direct fs helpers (Node)

@office-kit/pptx/node exposes loadPresentationFile / savePresentationToFile so you can skip the readFile / writeFile glue:

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');

Browser via fetch

The default entry is browser-safe (no node:fs). Load straight from a fetch Response:

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));
}

This works in any environment with fetch: modern browsers, Bun, Deno, Cloudflare Workers, edge runtimes.

What’s next

  • Recipes — copy-pasteable code for the most common tasks (charts, tables, comments, animations, transitions, validation).
  • Cheatsheet — one-page lookup: "I want to do X" → exact functions to call.
  • API reference — every export, organized by section.
  • GitHub: office-kit/pptx