Generate & edit PowerPoint .pptx from Node and the browser.

Full presentation model — slides, shapes, text, tables, charts, themes, notes, comments, transitions, animations — built around round-trip fidelity. Output validates against the ECMA-376 XSDs and opens cleanly in PowerPoint, Keynote, Google Slides, and LibreOffice. No native modules.

$ pnpm add @office-kit/pptx / npm i @office-kit/pptx
slides 100 synthetic deck saves in ~25 ms
bundle ~61 KB minimal load → save, unminified
targets Node + browser no PowerPoint, no native modules

What it does well

04 / 04
01

Round-trips real decks

Load a .pptx from PowerPoint, Keynote, Google Slides, or LibreOffice and save it back without corruption. Unknown parts — SmartArt, OLE, custom extensions — are preserved verbatim so the original consumer still opens them.

02

Author on top of any template

180+ preset shapes, custom text formatting, tables, embedded charts (column / line / bar / pie / doughnut / area) with auto-generated xlsx, solid / gradient / pattern / image fills, shadows, glows, rotation, z-order.

03

Template editing, first-class

Token replace (`{{name}}` → `Yamashita`) across slides and notes, image swap with geometry preserved, slide CRUD, placeholder inheritance from layout/master, hyperlinks, comments, transitions, animations.

04

Tiny & tree-shakeable

Minimal load → save bundle ~61 KB unminified, full fn-API ~122 KB. One ESM build runs in Node and the browser. Every export is side-effect-free; bundlers drop what you do not import.

Two snippets to get the shape

live · type-checked

Both files below live under site/src/lib/examples/ and are type-checked against the real library on every build — if an API renames, the site build fails.

01

Token-based template fill

Replace `{{tokens}}` across every slide and the speaker notes — the canonical full-library round-trip.

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

Build a deck from a blank template

Add slides on a layout, drop in a text box, an image, and a chart, then save.

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

More in Getting started & Recipes.