Skip to content

Viewer Instance API

createPptxViewer returns a PptxViewerInstance, the imperative equivalent of the other bindings' template refs / handles. All toolbar operations are also available as instance methods, so you can hide the chrome (showToolbar: false, showThumbnails: false) and drive the viewer from your own UI. The interface extends the shared PowerPointViewerAPI implemented by every binding, so the getContent / goTo / getSlides-style methods below match the React, Vue, Angular, and Svelte handles.

ts
import { createPptxViewer, type PptxViewerInstance } from 'pptx-vanilla-viewer';

const viewer: PptxViewerInstance = createPptxViewer(host, { source });

Loading

MethodSignatureDescription
loadFile(file: Blob | ArrayBuffer | Uint8Array) => Promise<void>Load a presentation from bytes or a Blob/File (replaces the current one).
loadUrl(url: string) => Promise<void>Fetch and load a presentation from a URL.

Both resolve once the presentation is rendered; failures surface through the onError callback.

MethodSignatureDescription
next / goNext() => voidGo to the next slide (no-op on the last slide).
prev / goPrev() => voidGo to the previous slide (no-op on the first slide).
goToSlide / goTo / setActiveSlideIndex(index: number) => voidJump to a zero-based slide index (clamped).
getSlideCount() => numberNumber of slides in the loaded presentation (0 when none).
getCurrentSlide / getActiveSlideIndex() => numberZero-based index of the visible slide.

The aliases (goTo, goPrev, goNext, getActiveSlideIndex, setActiveSlideIndex) come from the shared PowerPointViewerAPI and behave identically to the vanilla-native names.

Zoom

MethodSignatureDescription
getZoom() => numberEffective zoom scale (1 = 100%), after fit resolution.
setZoom(zoom: number) => voidSet an explicit zoom scale.
zoomIn() => voidZoom in by one step.
zoomOut() => voidZoom out by one step.
zoomToFit() => voidFit the slide to the viewport.
zoomReset() => voidReset zoom to 100%.

Viewer mode

MethodSignatureDescription
getMode() => ViewerModeThe current mode, derived from state.
setMode(mode: ViewerMode) => voidSwitch mode; see the mapping below.
ts
type ViewerMode = 'preview' | 'edit' | 'present' | 'master';

setMode('present') enters presentation mode; 'edit' enables editing; 'master' enables editing and switches to the master view; 'preview' leaves presentation/master mode and disables editing.

Theming & localization

MethodSignatureDescription
setTheme(theme: ViewerTheme | undefined) => voidApply a new viewer theme (pass undefined to reset to defaults). See Theming.
setLocale(locale: string) => voidSwitch the UI locale (rebuilds the chrome labels).

Presentation mode

MethodSignatureDescription
enterPresentation() => Promise<void>Enter presentation mode (real Fullscreen API).
exitPresentation() => Promise<void>Exit presentation mode (Esc also exits).

Entering and leaving fires the onPresentationChange callback.

Editing

Pass editable: true in the options (or call setEditable(true)) to turn on click-to-select, drag-to-move, resize/rotate handles, and double-click inline text editing directly in the DOM. These methods are the programmatic entry points around that interaction:

MethodSignatureDescription
setEditable(editable: boolean) => voidEnable or disable editing at runtime (disabling clears the selection).
setEditTemplateMode(enabled: boolean) => voidTarget inherited master/layout elements on the current slide.
undo() => voidUndo the last edit (no-op when the undo stack is empty).
redo() => voidRedo the last undone edit (no-op when the redo stack is empty).
canUndo / canRedo() => booleanWhether undo() / redo() would do anything.
deleteSelected() => voidDelete the selected element (no-op without a selection).
getSelectedElementId() => string | nullId of the selected element, or null.
isDirty() => booleanWhether the document has unsaved changes.

onChange fires after any mutation (move, resize, rotate, text edit, delete, undo, redo); onDirtyChange fires when the unsaved-edits flag flips; onSelectionChange fires when the selected element id changes.

Keyboard shortcuts, active whenever an element is selected and editing is enabled: Ctrl/Cmd+Z undo, Ctrl/Cmd+Shift+Z (or Ctrl+Y) redo, Delete/Backspace delete, Ctrl/Cmd+D duplicate, arrow keys nudge 1px (Shift+arrow for 10px), Escape deselect.

Saving & downloads

MethodSignatureDescription
save(format?: PptxSaveFormat) => Promise<Uint8Array>Serialise the (edited) presentation (default 'pptx') and clear the dirty flag.
getContent() => Promise<Uint8Array>Alias of save(): the serialized .pptx bytes (shared-API name).
downloadAs(format: PptxSaveFormat, fileName?: string) => Promise<void>Save and trigger a browser download in a supported OpenXML format.
downloadPptx(fileName?: string) => Promise<void>save() plus trigger a browser download (default presentation.pptx).
packageForSharing(fileName?: string) => Promise<void>Bundle the current presentation and usage notes in a shareable ZIP download.
ts
type PptxSaveFormat = 'pptx' | 'ppsx' | 'pptm';
ts
const viewer = createPptxViewer(host, { source, editable: true });

undoButton.addEventListener('click', () => viewer.undo());
redoButton.addEventListener('click', () => viewer.redo());
saveButton.addEventListener('click', () => void viewer.downloadPptx('edited.pptx'));

Export & print

Raster export renders each slide off-screen at scale 1 and rasterises it with html2canvas-pro (dynamically imported, so the first call pays a one-time load cost). jspdf and the GIF encoder are also lazy-loaded. Only one export runs at a time; a call while one is in flight resolves as a no-op.

MethodSignatureDescription
exportSlidePng(index?: number) => Promise<void>Export a slide as a PNG download (defaults to the current slide).
copySlideAsImage(index?: number) => Promise<void>Copy a slide to the system clipboard as a PNG image.
exportPdf(options?: ExportPdfOptions) => Promise<void>Export every slide as a multi-page PDF download (one slide per page).
exportGif(options?: ExportGifOptions) => Promise<void>Export every slide as an animated GIF download (one frame per slide, shared pure-JS GIF89a encoder).
exportVideo(options?: ExportVideoOptions) => Promise<void>Export every slide as a WebM video download (canvas stream recorded by MediaRecorder).
print(options?: PrintOptions) => Promise<boolean>Assemble the printable document and open it in a print window; false = popup blocked.

All option interfaces are exported from the package root:

ts
type ExportProgress = (current: number, total: number) => void;

interface ExportPdfOptions {
	onProgress?: ExportProgress; // capture-phase progress: (currentSlide, totalSlides)
	signal?: AbortSignal; // abort early; checked between slides
}

interface ExportGifOptions {
	slideDurationMs?: number; // per-frame duration, default 2000
	slideTimingsMs?: number[]; // per-slide overrides (e.g. rehearsed timings)
	maxDimension?: number; // cap on the longer frame side, default 1920
	onProgress?: ExportProgress;
	signal?: AbortSignal;
}

interface ExportVideoOptions {
	slideDurationMs?: number; // per-slide hold, default 3000
	slideTimingsMs?: number[]; // per-slide overrides
	fps?: number; // recording frame rate, default 30
	videoBitsPerSecond?: number; // MediaRecorder bitrate, default 5,000,000
	onProgress?: ExportProgress; // capture phase
	onRecordProgress?: ExportProgress; // recording phase
	signal?: AbortSignal;
}

Downloads are named presentation-slide-<n>.png, presentation.pdf, presentation.gif, and presentation.webm. Aborting via signal rejects with an AbortError DOMException.

renderToCanvas

A standalone function exported from the package root (no viewer instance needed) that rasterises any DOM element to a Canvas:

ts
import { renderToCanvas } from 'pptx-vanilla-viewer';

const canvas: HTMLCanvasElement = await renderToCanvas(element, { scale: 2 });
const dataUrl = canvas.toDataURL('image/png');
ts
function renderToCanvas(
	element: HTMLElement,
	options?: Partial<Html2CanvasOptions>, // the html2canvas-pro Options type
): Promise<HTMLCanvasElement>;

The same html2canvas-pro wrapper React, Vue, Angular and Svelte export. Reach for it rather than calling html2canvas yourself: during the onclone phase it runs the shared CSS-preprocessing passes, which convert modern colour functions (oklch / oklab / lch / lab / color()) to sRGB and flatten backdrop-filter, mix-blend-mode and CSS 3D transforms. The viewer's theme tokens are authored in oklch, which html2canvas cannot parse on its own.

Print

print() covers slides, notes pages, handouts, and an outline view, assembled from the shared print module. PrintOptions is any subset of the shared PrintSettings (unspecified fields fall back to the defaults: all slides, landscape, full colour) plus progress/abort and a window override:

ts
interface PrintOptions extends Partial<PrintSettings> {
	onProgress?: ExportProgress;
	signal?: AbortSignal;
	openPrintWindow?: (htmlDocument: string) => boolean; // OpenPrintWindow
}

interface PrintSettings {
	printWhat: 'slides' | 'handouts' | 'notes' | 'outline'; // default 'slides'
	orientation: 'portrait' | 'landscape'; // default 'landscape'
	colorMode: 'color' | 'grayscale' | 'blackAndWhite'; // default 'color'
	frameSlides: boolean; // default false
	slidesPerPage: 1 | 2 | 3 | 4 | 6 | 9; // handouts only, default 6
	slideRange: 'all' | 'current' | 'custom'; // default 'all'
	customRangeFrom: number; // 1-based, default 1
	customRangeTo: number; // 1-based, default 1
}

Popup blockers

The default opener uses window.open, which browsers typically only allow inside a user gesture: call print() from a click handler, or pass a custom openPrintWindow that writes into an iframe you own. When the popup is blocked the promise resolves false.

SVG export (standalone functions)

Vector export does not need the viewer instance; two pure functions work on the parsed core data (reach it via getHandler() or getSlides()):

ts
import { exportSlideToSvg, exportAllSlidesToSvg } from 'pptx-vanilla-viewer';

exportSlideToSvg(slide, width, height, options?): string; // one slide as SVG markup
exportAllSlidesToSvg(data, options?): string[]; // PptxData in, one SVG string per slide

interface SvgExportOptions {
	includeHidden?: boolean; // include hidden slides when exporting all, default false
	slideIndices?: number[]; // 0-based subset; omitted = all slides
	defaultFontFamily?: string;
	defaultFontSize?: number; // points
}

Slides & elements (data API)

The shared data surface for hosts that build their own UI. Slide getters return the actual typed PptxSlide[] / PptxElement[] model as read-only snapshots; mutations only flow back through the manipulation methods (which participate in undo/redo and fire onChange).

MethodSignatureDescription
getSlides() => readonly PptxSlide[]The full slide array.
getSlide(index: number) => PptxSlide | undefinedOne slide by zero-based index.
getActiveSlide() => PptxSlide | undefinedThe currently active slide.
addSlide(afterIndex?: number) => voidAdd a blank slide after the given index (default: at the end).
deleteSlides(indexes: number[]) => voidDelete slides at the given indexes (at least one slide is kept).
duplicateSlides(indexes: number[]) => voidDuplicate slides at the given indexes.
moveSlide(fromIndex: number, toIndex: number) => voidMove a slide to a new position.
toggleHideSlides(indexes: number[]) => voidToggle the hidden flag on slides.
getElements(slideIndex?: number) => readonly PptxElement[]Elements on a slide (default: active slide).
getElementById(elementId: string, slideIndex?: number) => PptxElement | undefinedOne element by id.
updateElement(elementId: string, updates: Partial<PptxElement>) => voidPatch element properties (e.g. { x: 100, width: 300 }).
deleteElements(elementIds: string[]) => voidDelete elements by id from the active slide.
duplicateElement(elementId: string) => string | undefinedDuplicate an element; returns the new element's id.
getSelectedElementIds() => string[]Ids of the currently selected elements.
selectElements(ids: string[]) => voidProgrammatically select elements.
clearSelection() => voidClear the selection.

Collaboration

MethodSignatureDescription
startCollaboration(config: CollaborationConfig) => Promise<void>Start (or restart) a real-time session; resolves once the transport is created. Status arrives via onCollaborationStatus.
stopCollaboration() => voidStop the active session (no-op when none is running).
getCollaborationStatus() => ConnectionStatusCurrent status: 'disconnected' | 'connecting' | 'connected' | 'error' ('disconnected' when inactive).

See Options for CollaborationConfig and the wire-format caveat.

Autosave

MethodSignatureDescription
autosaveNow() => Promise<void>Force an immediate snapshot (no-op when autosave is disabled).
setAutosaveEnabled(enabled: boolean) => voidEnable or disable recovery autosave without rebuilding.
isAutosaveEnabled() => booleanWhether recovery autosave is currently enabled.

Extension & escape hatches

MethodSignatureDescription
getRegistry() => ElementRendererRegistryThe element-renderer registry in effect (extension point). See Element Renderers.
getHandler() => PptxHandler | nullThe live pptx-viewer-core handler for the loaded file (or null).

Core escape hatch

getHandler() exposes the full pptx-viewer-core PptxHandler behind the viewer, which allows operations the viewer itself does not surface: convert the deck to Markdown or read parts of the underlying archive. (For plain serialisation prefer the instance's own save() / getContent(), which also clear the dirty flag.)

ts
const handler = viewer.getHandler();
if (handler) {
	const bytes = await handler.save(handler.pptxData!.slides); // Uint8Array
}

Teardown

MethodSignatureDescription
destroy() => voidTear down DOM, listeners, Blob URLs, and the core handler.

Example: external controls

ts
import { createPptxViewer } from 'pptx-vanilla-viewer';

const viewer = createPptxViewer(document.getElementById('host')!, {
	source: '/deck.pptx',
	showToolbar: false,
	showThumbnails: false,
	onSlideChange: (i) => {
		counter.textContent = `Slide ${i + 1} of ${viewer.getSlideCount()}`;
	},
});

prevButton.addEventListener('click', () => viewer.prev());
nextButton.addEventListener('click', () => viewer.next());
fitButton.addEventListener('click', () => viewer.zoomToFit());
presentButton.addEventListener('click', () => void viewer.enterPresentation());
pdfButton.addEventListener('click', () => void viewer.exportPdf());

Openable file kinds

The package root re-exports the shared answer to "can the viewer open this file?", so a host's drop target and its <input accept> cannot disagree with the loader. Hand-rolled endsWith chains drift: every demo in this repo once shipped .pptx,.ppt,.json, which refused on drop a .pptm that File > Open inside the viewer accepted without complaint.

ts
import {
	PPTX_OPEN_ACCEPT,
	PRESENTATION_OPEN_EXTENSIONS,
	isSupportedPresentationFile,
	isLegacyBinaryPresentation,
	presentationBaseName,
	savedPresentationFileName,
	type SavedPresentationFormat,
} from 'pptx-vanilla-viewer';
ExportTypeDescription
PPTX_OPEN_ACCEPTstringReady-made <input type="file" accept> value: .pptx,.ppsx,.pptm,.potx,.ppt,.json.
PRESENTATION_OPEN_EXTENSIONSreadonly string[]The same list unjoined, for a drop target that wants to test extensions itself.
isSupportedPresentationFile(name?: string | null) => booleanCheap pre-filter for a picked or dropped file name. Extension-only; the real answer is the loader's sniff.
isLegacyBinaryPresentation(name?: string | null) => booleanTrue for the binary PowerPoint 97-2003 family (.ppt / .pps / .pot), which the viewer reads but never writes.
presentationBaseName(name?: string | null, fallback?: string) => stringThe file-name stem, directories and any loadable extension removed (a path like decks/report.ppt becomes report).
savedPresentationFileName(name?: string | null, format?: SavedPresentationFormat) => stringThe name a saved copy should be offered under: report.ppt becomes report.pptx.
SavedPresentationFormat'pptx' | 'ppsx' | 'pptm'The formats the save path can produce. Binary .ppt is deliberately absent: output is always OpenXML.

savedPresentationFileName is the one that matters on Save As. Output is always an OpenXML package, so keeping a legacy source extension would hand the user a .ppt whose bytes are a ZIP, which PowerPoint refuses to open.

Released under the Apache-2.0 License.