Instance API
The component instance obtained through bind:this implements PowerPointViewerApi: the shared cross-binding viewer contract (the same one behind React's ref handle and Vue's defineExpose) plus the Svelte binding's editing and export methods. 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.
<script lang="ts">
import { PowerPointViewer, type PowerPointViewerApi } from 'pptx-svelte-viewer';
let { bytes }: { bytes: Uint8Array } = $props();
let viewer = $state<PowerPointViewerApi>();
</script>
<PowerPointViewer source={bytes} bind:this={viewer} />Snapshots, not stores
The getter methods (canUndo(), getZoom(), getSelectedElementIds(), ...) return plain snapshots; they are not reactive stores. To react to changes, use the callback props (onzoomchange, onselectionchange, ondirtychange, ...) from Component Props.
Serialisation
| Method | Signature | Description |
|---|---|---|
getContent | () => Promise<Uint8Array> | Serialise the current presentation to .pptx bytes (alias of save()). |
Navigation
| Method | Signature | Description |
|---|---|---|
goTo | (index: number) => void | Jump to a zero-based slide index (clamped). |
goPrev | () => void | Go to the previous slide. |
goNext | () => void | Go to the next slide. |
getActiveSlideIndex | () => number | Zero-based index of the visible slide. |
setActiveSlideIndex | (index: number) => void | Alias of goTo. |
getSlideCount | () => number | Number of slides in the loaded presentation. |
Zoom
| Method | Signature | Description |
|---|---|---|
getZoom | () => number | Effective zoom scale (1 = 100%). |
setZoom | (level: number) => void | Set an explicit zoom scale (clamped). |
zoomIn | () => void | Zoom in by one step. |
zoomOut | () => void | Zoom out by one step. |
zoomReset | () => void | Reset zoom to 100%. |
Mode and presentation
| Method | Signature | Description |
|---|---|---|
getMode | () => ViewerMode | Current mode: 'preview' | 'edit' | 'present' | 'master'. |
setMode | (mode: ViewerMode) => void | Switch mode. 'present' enters fullscreen presentation (real Fullscreen API); any other mode exits it. 'edit' and 'master' imply editing. |
viewer?.setMode('present'); // start presenting; Esc exitsSlide access and manipulation
| Method | Signature | Description |
|---|---|---|
getSlides | () => readonly PptxSlide[] | The full slide array (snapshot with full type information). |
getSlide | (index: number) => PptxSlide | undefined | A single slide by zero-based index. |
getActiveSlide | () => PptxSlide | undefined | The currently active slide. |
addSlide | (afterIndex?: number) => void | Add a blank slide after the given index (or at the end). |
deleteSlides | (indexes: number[]) => void | Delete slides by index (at least one slide is kept). |
duplicateSlides | (indexes: number[]) => void | Duplicate slides at the given indexes. |
moveSlide | (fromIndex: number, toIndex: number) => void | Move a slide to a new position. |
toggleHideSlides | (indexes: number[]) => void | Toggle the hidden flag on slides. |
isDirty | () => boolean | Whether the document has unsaved changes. |
Element access and manipulation
| Method | Signature | Description |
|---|---|---|
getElements | (slideIndex?: number) => readonly PptxElement[] | Elements on a slide (defaults to the active slide). |
getElementById | (id: string, slideIndex?: number) => PptxElement | undefined | A single element by id. |
updateElement | (id: string, updates: Partial<PptxElement>) => void | Patch element properties, e.g. { x: 100, width: 300 }. |
updateElements | (updates: readonly ElementUpdate[], options?: ElementUpdateOptions) => Promise<void> | Update elements across slides in one undo step. |
deleteElements | (ids: string[]) => void | Delete elements by id from the active slide. |
duplicateElement | (id: string) => string | undefined | Duplicate an element; returns the new element's id. |
Inserting an element
addElement(element: PptxElement): string | undefined appends a defensive copy to the active editable slide and selects it, returning its fresh ID. Coordinates are preserved; group descendants also receive fresh IDs. Pending text is committed through the existing editor path, with normal dirty-state and Undo/Redo behavior. Synchronous edits may share a history entry, but all insertions are retained. It returns undefined while loading, after a load error, without an active slide, or in read-only/protected, preview, presentation, or template/master editing modes.
Use a self-contained model or one from the current document. With a loaded viewer in edit mode:
import { createImageElement } from 'pptx-viewer-core';
const image = createImageElement(pngDataUrl, { x: 40, y: 40, width: 160, height: 90 });
const insertedId = viewer?.addElement(image);This method does not install clipboard listeners, fetch remote URLs, choose image dimensions, or import another document's relationships. A host-owned paste handler can read an image and call it. For a new data-URL image, use the factory above without inventing an imagePath, which denotes an existing archive part.
Loading a local image
The package also exports createImageElementFromFile(file, canvasSize, signal?). Given a local File or Blob, the slide size in pixels, and an optional AbortSignal:
import { createImageElementFromFile } from 'pptx-svelte-viewer';
const image = await createImageElementFromFile(file, canvasSize, signal);The helper preserves the image bytes and returns a centred ImagePptxElement, fitted to the slide without upscaling. It resolves null for invalid/unreadable images or dimensions, cancellation, or unavailable browser APIs. The helper does not use browser APIs until called; decoding requires those APIs.
This only constructs an element: it does not change a deck, history, selection, or clipboard. After await, verify that the same document and destination slide are still active and editable, then pass a non-null result to addElement. Abort pending work when that destination is abandoned. A slide ID alone is not a document identity, and decoding success does not guarantee that every browser image format round-trips in PowerPoint. No automatic paste listener is installed.
Selection
| Method | Signature | Description |
|---|---|---|
getSelectedElementIds | () => string[] | Ids of the currently selected elements. |
selectElements | (ids: string[]) => void | Programmatically select elements. |
clearSelection | () => void | Clear the selection. |
getSelectedElementId | () => string | null | The selected top-level element id, or null. |
Editing
Active when editable is set (see Getting Started > Editing).
| Method | Signature | Description |
|---|---|---|
undo | () => void | Undo the last committed edit. |
redo | () => void | Redo the last undone edit. |
canUndo | () => boolean | Whether an undo step is available (snapshot; not reactive). |
canRedo | () => boolean | Whether a redo step is available. |
deleteSelected | () => void | Delete the selected element (no-op when nothing is selected). |
save | (format?: PptxSaveFormat) => Promise<Uint8Array> | Serialise the edited slides to bytes ('pptx' | 'ppsx' | 'pptm'). |
downloadAs | (format: PptxSaveFormat, fileName?: string) => Promise<void> | Save + trigger a browser download in the given format. |
downloadPptx | (fileName?: string) => Promise<void> | Save + download as .pptx (default name). |
packageForSharing | (fileName?: string) => Promise<void> | Assemble and download the sharing package. |
Keyboard shortcuts, active whenever editing is enabled: Ctrl/Cmd+Z undo, Ctrl/Cmd+Shift+Z redo, Delete/Backspace delete, Ctrl/Cmd+D duplicate, arrow keys nudge (with Shift for larger steps), Escape deselect.
Export and print
| Method | Signature | Description |
|---|---|---|
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> | Multi-page PDF download, one slide per page. |
exportGif | (options?: ExportGifOptions) => Promise<void> | Animated GIF download. |
exportVideo | (options?: ExportVideoOptions) => Promise<void> | WebM video download. |
print | (options?: PrintOptions) => Promise<boolean> | Open the browser print dialog (slides / handouts / notes / outline). |
See Export & Print for the option shapes, pipelines, and the standalone SVG export functions.
UI customization
The handle also carries the whole ViewerCustomizationApi, so the chrome can be customised while the viewer runs. Each call re-renders the affected UI immediately.
viewer?.hideRibbonTab('draw');
viewer?.lockSetting('general.userName', 'Ada Lovelace');
viewer?.hideContextMenuCommand('delete');
viewer?.remapShortcut('duplicate', 'Mod+Shift+D');
viewer?.setFeatureEnabled('ai', false);
viewer?.setPanelVisible('notes', false);
viewer?.updateCustomization({ hiddenDialogs: ['print'] });
const current = viewer?.getCustomization();| Method | Effect |
|---|---|
getCustomization() / setCustomization(c) / updateCustomization(patch) / resetCustomization() | Read, replace, merge or clear the whole ViewerCustomization. |
hideRibbonTab / showRibbonTab, hideToolbarButton / showToolbarButton | Ribbon tabs and top-level toolbar buttons. |
hideOptionsPage, hideOptionsSection, hideSetting (and show*) | File > Options pages, sections and settings. |
lockSetting(id, value, hidden?) / unlockSetting(id) / setSettingDefault(id, value) | Pin a setting (read-only or hidden) or set a host default. |
hideBackstagePage / hideBackstageCard (and show*) | File tab pages and action cards. |
hideContextMenuCommand / hideCanvasContextMenuCommand (and show*) | Element and empty-canvas right-click entries. |
disableShortcut / enableShortcut / remapShortcut | Editor keyboard shortcuts. |
setPanelVisible, setFeatureEnabled, setDialogAvailable | Panels, feature areas (AI, collaboration, ...) and dialogs. |
The full id reference and recipes are in the UI Customization guide.
Example: external controls
<script lang="ts">
import { PowerPointViewer, type PowerPointViewerApi } from 'pptx-svelte-viewer';
let { bytes }: { bytes: Uint8Array } = $props();
let viewer = $state<PowerPointViewerApi>();
let current = $state(0);
let count = $state(0);
</script>
<PowerPointViewer
source={bytes}
showToolbar={false}
showThumbnails={false}
bind:this={viewer}
onload={({ slideCount }) => (count = slideCount)}
onslidechange={(index) => (current = index)}
/>
<div>
<button onclick={() => viewer?.goPrev()}>Prev</button>
<span>Slide {current + 1} of {count}</span>
<button onclick={() => viewer?.goNext()}>Next</button>
<button onclick={() => viewer?.setMode('present')}>Present</button>
</div>Lower-level building blocks
The pptx-svelte-viewer/viewer entry point additionally exports the viewer's internal framework-free state helpers (ViewerState, PresentationLoader, clampSlideIndex, fitScale, resolveNavigationKey, zoomInPercent, zoomOutPercent) for hosts building custom chrome on the same primitives. These are lower-level than the component API and not needed for typical embedding.
pptx-svelte-viewer/internals: slide-transition helpers
pptx-viewer-shared (the framework-agnostic logic every binding bundles) is a private, unpublished workspace package: it is never on npm, so code outside this monorepo cannot import from it directly. A host embedding its own presentation surface (a custom stage, outside the full PowerPointViewer) still needs the transition resolver/keyframes and overlay component, so they are re-exported from the pptx-svelte-viewer/internals subpath instead:
import {
resolveSlideTransition,
resolveTransitionDurationMs,
SLIDE_TRANSITION_KEYFRAMES,
PresentationTransitionOverlay,
} from 'pptx-svelte-viewer/internals';resolveSlideTransition maps a PptxSlideTransition to the CSS animation shorthands for the outgoing/incoming layers; resolveTransitionDurationMs resolves its effective duration (ms), honoring an authored duration, the legacy spd token, and PowerPoint's own defaults; SLIDE_TRANSITION_KEYFRAMES (alias SLIDE_TRANSITION_KEYFRAMES_CSS) is the @keyframes block those animation names reference, injected once via a <style> element. Also exported: getSlideTransitionAnimations, getCinematicTransitionAnimations, getP14TransitionAnimations (the classic / cinematic / exotic sub-resolvers), CINEMATIC_TRANSITION_KEYFRAMES / P14_TRANSITION_KEYFRAMES_ALL (their keyframe sub-blocks), resolveDirection / resolveDirection8 / resolveOrientation / resolveWheelSpokeCount, the supporting constants (RANDOM_ELIGIBLE_TYPES, INSTANT, DEFAULT_TRANSITION_DURATION_MS, DEFAULT_MORPH_DURATION_MS, TRANSITION_SPEED_DURATION_MS, EASE, WHEEL_SPOKE_COUNTS), and the PresentationTransitionOverlay component itself.
As with every other binding's internals entry, this is not covered by semver: reach for it only when the curated pptx-svelte-viewer / pptx-svelte-viewer/viewer exports genuinely cannot do what you need, and pin an exact version if you depend on it.
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.
import {
PPTX_OPEN_ACCEPT,
PRESENTATION_OPEN_EXTENSIONS,
isSupportedPresentationFile,
isLegacyBinaryPresentation,
presentationBaseName,
savedPresentationFileName,
type SavedPresentationFormat,
} from 'pptx-svelte-viewer';| Export | Type | Description |
|---|---|---|
PPTX_OPEN_ACCEPT | string | Ready-made <input type="file" accept> value: .pptx,.ppsx,.pptm,.potx,.ppt,.json. |
PRESENTATION_OPEN_EXTENSIONS | readonly string[] | The same list unjoined, for a drop target that wants to test extensions itself. |
isSupportedPresentationFile | (name?: string | null) => boolean | Cheap pre-filter for a picked or dropped file name. Extension-only; the real answer is the loader's sniff. |
isLegacyBinaryPresentation | (name?: string | null) => boolean | True for the binary PowerPoint 97-2003 family (.ppt / .pps / .pot), which the viewer reads but never writes. |
presentationBaseName | (name?: string | null, fallback?: string) => string | The file-name stem, directories and any loadable extension removed (a path like decks/report.ppt becomes report). |
savedPresentationFileName | (name?: string | null, format?: SavedPresentationFormat) => string | The 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.