Composable primitives for viewing and editing PowerPoint presentations in the browser. Unstyled, accessible, and entirely yours to build on.
Visit pptx.diceui.com to view the full documentation.
| Package | Description |
|---|---|
@diceui/pptx |
Composable primitives for viewing and editing PowerPoint presentations in the browser |
@diceui/pptx-core |
PPTX engine for parsing, rendering, editing, and saving PowerPoint presentations in the browser |
# React
npm install @diceui/pptx
# Core only (no React dependency)
npm install @diceui/pptx-coreimport * as Presentation from "@diceui/pptx";
export function Viewer({ file }: { file: File }) {
return (
<Presentation.Root file={file}>
<Presentation.ThumbnailList />
<Presentation.Viewport>
<Presentation.Slide />
</Presentation.Viewport>
</Presentation.Root>
);
}import * as Presentation from "@diceui/pptx";
export function Editor({ file }: { file: File }) {
return (
<Presentation.Root file={file} readOnly={false}>
<Presentation.ThumbnailList />
<Presentation.Viewport autoFit>
<Presentation.Slide>
<Presentation.Selection />
</Presentation.Slide>
</Presentation.Viewport>
</Presentation.Root>
);
}import { buildPresentation, readPptx, renderSlide } from "@diceui/pptx-core";
const files = await readPptx(arrayBuffer);
const presentation = buildPresentation(files);
const slide = presentation.slides[0];
const handle = renderSlide(presentation, slide);
document.body.appendChild(handle.element);
// When done
handle.dispose();The context provider. Accepts a File, Blob, ArrayBuffer, or Uint8Array as file.
Scrollable canvas area. Renders the active slide scaled to fit.
Renders the active slide. Must be inside Presentation.Viewport.
Editing overlay. Enables drag-to-move, resize (with Shift for aspect-ratio lock), inline text editing, multi-select, marquee selection, and keyboard shortcuts. Must be placed inside Presentation.Slide (as its child) so it can overlay the slide surface.
Interaction model:
| Action | Description |
|---|---|
| Click text box / placeholder | Select + enter text mode |
| Click regular shape | Select |
| Double-click regular shape | Enter text mode |
| Type while shape selected | Enter text mode |
| Drag shape | Move |
| Shift + drag shape | Move along one axis, the one the drag favors |
| Drag border of text box | Move while keeping text mode |
| Drag resize handle | Resize, applied to every selected shape |
| Shift + drag corner handle | Resize preserving aspect ratio |
| Ctrl/Cmd+A | Select all |
| Shift/Ctrl/Cmd+click | Toggle shape in selection |
| Drag empty canvas | Marquee select |
| Shift + drag empty canvas | Marquee select, adding to the current selection |
| Delete / Backspace | Delete selected shape(s) |
| Arrow keys | Nudge (1 px; Shift = 10 px) |
| Ctrl/Cmd+Z | Undo |
| Ctrl/Cmd+Y / Ctrl/Cmd+Shift+Z | Redo |
| Escape | Deselect / exit text mode |
Undo and redo are the exception: they are opt-in via undoRedoShortcuts. Everything else in the table is always bound.
Scrollable list of slide thumbnails. Each ThumbnailItem is mounted immediately, while its preview is rendered lazily as it approaches the viewport and cached. Compose your own item with Presentation.ThumbnailItem, Presentation.ThumbnailItemNumber, and Presentation.ThumbnailItemPreview, or render it childless for the default.
Slot components for custom loading and error states.
Optional. Root creates its own store, so this is only needed when you want to own the store and drive it from outside the tree.
const store = useCreatePresentationStore();
<Presentation.Provider store={store}>
<Presentation.Root file={file}>{/* ... */}</Presentation.Root>
</Presentation.Provider>;const { presentation, status } = usePresentation();
const { slide } = useSlide();
const { zoom, isAutoFit, setZoom, setAutoFit } = useZoom();
const { canUndo, canRedo, isDirty, undo, redo } = useHistory();
const store = useCreatePresentationStore();All mutations go through store.edit(operation) and support undo/redo via store.undo() / store.redo().
| Operation | Description |
|---|---|
setTextRun |
Replace a single text run's content |
setTextBody |
Replace all paragraphs and runs in a shape |
setNodeTransform |
Move / resize a shape |
setSolidFill |
Change a shape's fill color |
deleteNode |
Delete a shape from a slide |
moveSlide |
Reorder slides |
duplicateSlide |
Duplicate a slide |
deleteSlide |
Delete a slide |
batch |
Group multiple operations into one undoable step |
pnpm install
pnpm dev # starts apps/docs on http://localhost:3000
pnpm build # build all packages
pnpm test # run all tests
pnpm typecheck # TypeScript type check
pnpm check # lint + typecheck + format (oxlint + tsc + oxfmt)pptx/
├── apps/
│ ├── docs/ # Next.js docs + interactive playground
│ └── video/ # Remotion video renderer
└── packages/
├── core/ # @diceui/pptx-core (OOXML parse, render, edit, save)
├── react/ # @diceui/pptx (React primitives)
├── ui/ # @pptx/ui (shared shadcn/ui components)
├── e2e/ # @diceui/pptx-e2e (visual regression tests)
└── config/ # Shared TypeScript / build config
See packages/core/OOXML-SUPPORT.md for a detailed feature matrix against ECMA-376.
- pptx-renderer (Apache-2.0): the parser was originally derived from this work and has been substantially modified, extended, and refactored.
- LibreOffice (MPL-2.0): the predefined table style data (
packages/core/src/renderer/table-style.ts) is derived frompredefined-table-styles.cxx.
The embedded font decoder (packages/core/src/fonts/mtx/) is an original implementation of the W3C MTX and EOT Member Submissions. See PROVENANCE.md.
Apache-2.0, except for the files noted below.
- The predefined table style data (
packages/core/src/renderer/table-style.ts) stays under MPL-2.0, as marked in its file header. - The fonts embedded in the sample decks under
apps/video/public/andpackages/e2e/fixtures/are licensed under OFL-1.1.
See NOTICE for the full third-party attributions.
