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.
// 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:
// 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.
// 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:
// 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:
// 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