# pptx-vue-viewer

[![npm version](https://img.shields.io/npm/v/pptx-vue-viewer.svg)](https://www.npmjs.com/package/pptx-vue-viewer)
[![license](https://img.shields.io/npm/l/pptx-vue-viewer.svg)](https://github.com/ChristopherVR/pptx-viewer/blob/main/LICENSE)

Show, edit, and present Microsoft PowerPoint (`.pptx`) files directly in a
Vue 3 app: no server, no conversion step, no PowerPoint install required. Drop
in a `<PowerPointViewer>` component, hand it the file's bytes, and it renders
slides as real HTML and CSS with full editing and export support.

![Navigating between slides in the Vue 3 demo](https://raw.githubusercontent.com/ChristopherVR/pptx-viewer/main/.github/assets/packages/vue-demo.gif)

The rendering is done by the framework-agnostic [`pptx-viewer-core`](https://www.npmjs.com/package/pptx-viewer-core) engine, which turns a `.pptx` file into a structured slide model. This package is the Vue layer that draws that model on screen, and the engine is **bundled in**, so you install just one package.

<samp>**[▶️ Try the live demo](https://christophervr.github.io/pptx-viewer/demo-vue/)** · **[📦 npm](https://www.npmjs.com/package/pptx-vue-viewer)** · **[📖 Full docs](https://christophervr.github.io/pptx-viewer/)** · **[🧩 Core SDK](https://www.npmjs.com/package/pptx-viewer-core)**</samp>

## Features

- **A single component**: `<PowerPointViewer>`, written in `<script setup>` style.
- **Real HTML rendering**: slides are drawn as ordinary HTML and SVG, not as a
  picture, so text stays sharp at any zoom and is selectable and accessible.
- **Editing**: select, drag, resize, rotate; inline text editing; format painter;
  shape adjustment handles; align, distribute, group, flip, and z-order; undo/redo;
  snap-to-grid, snap-to-shape, H/V guides, and rulers.
- **Full Office-style ribbon**: all tabs wired (Home, Insert, Draw, Design,
  Transitions, Animations, Slide Show, Review, View) plus a status bar and
  context menu.
- **Inspector**: element and slide property panels, including chart data editor.
- **Presentation mode**: animation playback, presenter view, slide transitions,
  rehearse timings, subtitles, and freehand ink.
- **Export**: PNG, PDF, GIF, and WebM video; print; Save As (pptx/ppsx/pptm).
- **Collaboration**: real-time Yjs-based co-editing with cursor/selection presence.
- **Comments, find/replace, accessibility panel, version history**, and more.
- **Mobile chrome**: touch toolbar, bottom bar with sheets, and touch editing.
- **Slide navigation**: live thumbnail previews, previous/next, and a slide counter.
- **Zoom**: in, out, and reset.
- **Themeable**: change colours through CSS custom properties.
- **Loads from anywhere**: an `ArrayBuffer` or `Uint8Array` from a file input,
  a `fetch`, drag-and-drop, and so on.

## Installation

```bash
npm install pptx-vue-viewer
```

**Peer requirements:** Vue 3.5+, `vue-i18n` (all UI labels go through it, see
[Localization](#localization-i18n)), and the engine's `jszip` /
`fast-xml-parser` peers:

```bash
npm install vue vue-i18n jszip fast-xml-parser
```

**Optional:** `three` enables interactive GLB/GLTF 3D models and the
`smartArt3D` and 3D chart renderers; without it those elements fall back to poster images /
flat SVG.

The `pptx-viewer-core` engine is **bundled in**, so you don't install it
separately unless you want to call the SDK directly.

## Usage

```vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { PowerPointViewer, type PowerPointViewerExpose } from 'pptx-vue-viewer';

// Base chrome styles (toolbar, thumbnails, layout). Import once.
import 'pptx-vue-viewer/styles';

const content = ref<Uint8Array>();
const viewer = ref<PowerPointViewerExpose>();

onMounted(async () => {
	const res = await fetch('/example.pptx');
	content.value = new Uint8Array(await res.arrayBuffer());
});

function onSlide(index: number) {
	console.log('active slide', index);
}
</script>

<template>
	<PowerPointViewer
		v-if="content"
		ref="viewer"
		:content="content"
		:theme="{ colors: { primary: '#6366f1' } }"
		@active-slide-change="onSlide"
		style="height: 100vh"
	/>
</template>
```

### Loading from a file input

```vue
<script setup lang="ts">
import { ref } from 'vue';
const content = ref<ArrayBuffer>();

async function onFile(event: Event) {
	const file = (event.target as HTMLInputElement).files?.[0];
	if (file) content.value = await file.arrayBuffer();
}
</script>

<template>
	<input type="file" accept=".pptx" @change="onFile" />
</template>
```

### Theming

Pass a partial `theme`; unset tokens fall back to the built-in dark palette.
Values accept any CSS color (`hex`, `rgb()`, `hsl()`, `oklch()`, …) and map to
`--pptx-*` CSS custom properties (shadcn/ui token names).

```ts
import type { ViewerTheme } from 'pptx-vue-viewer';

const theme: ViewerTheme = {
	colors: { primary: '#6366f1', background: '#0b1020' },
	radius: '0.5rem',
};
```

For app-wide theming you can also provide a theme to a subtree:

```ts
import { provideViewerTheme } from 'pptx-vue-viewer';
// call inside a parent component's setup()
provideViewerTheme({ colors: { primary: '#6366f1' } });
```

Two ready-made presets ship with the package: `vermilionLightTheme` (warm paper
canvas) and `vermilionDarkTheme` (dimmed presenter room), the same vermilion
brand look as the [documentation site](https://christophervr.github.io/pptx-viewer/):

```ts
import { vermilionLightTheme } from 'pptx-vue-viewer';
// <PowerPointViewer :theme="vermilionLightTheme" … />
```

The underlying palettes (`vermilionLightColors`, `vermilionDarkColors`) and
radius (`vermilionRadius`) are exported too for deriving your own variant.

### Reading the current presentation back

`getContent()` turns the current presentation back into `.pptx` bytes. Reach it
through a template `ref`:

```ts
const viewer = ref<PowerPointViewerExpose>();

async function save() {
	const bytes = await viewer.value!.getContent();
	// write `bytes` (Uint8Array) to a Blob / download / upload
}
```

### Viewport fit options

```vue
<PowerPointViewer :content="content" :fit-padding="0" :max-fit-scale="null" />
```

`fitPadding` is a per-side CSS-pixel number or `{ horizontal, vertical }`;
`maxFitScale` is a positive fit-factor ceiling or `null` for unlimited enlargement.
Omission preserves 8 px horizontal / 16 px vertical padding and a ceiling of 1.
The same props are available on `SlideCanvas`. Rulers reserve their existing
space separately. These options do not change document geometry or user zoom.
See the [cross-binding defaults](../../docs/guide/viewport-fit.md).

## API

### Props

| Prop                                                                       | Type                                 | Default                | Description                                                                                                                                                                                                                                                                                                                                                     |
| -------------------------------------------------------------------------- | ------------------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `content`                                                                  | `Uint8Array \| ArrayBuffer`          | n/a                    | The `.pptx` bytes to render. **Required.**                                                                                                                                                                                                                                                                                                                      |
| `theme`                                                                    | `ViewerTheme`                        | n/a                    | Color/radius overrides applied as CSS custom properties. Always wins over the File > Options theme picker.                                                                                                                                                                                                                                                      |
| `class`                                                                    | `string`                             | n/a                    | Class applied to the root element.                                                                                                                                                                                                                                                                                                                              |
| `canEdit`                                                                  | `boolean`                            | `false`                | Enables the editor toolbar, inspector, and drag-and-drop editing.                                                                                                                                                                                                                                                                                               |
| `filePath`                                                                 | `string`                             | n/a                    | Original file path, used for autosave recovery and version history.                                                                                                                                                                                                                                                                                             |
| `fileName`                                                                 | `string`                             | n/a                    | Display name of the open document, shown in the title bar.                                                                                                                                                                                                                                                                                                      |
| `fonts`                                                                    | `ViewerFontSource[]`                 | n/a                    | Licensed font sources supplied by the host application.                                                                                                                                                                                                                                                                                                         |
| `autosave`                                                                 | `boolean`                            | `true`                 | Enables debounced autosave (emits `@autosave` with serialised bytes).                                                                                                                                                                                                                                                                                           |
| `autosaveIntervalMs`                                                       | `number`                             | File > Options cadence | Autosave debounce window in milliseconds.                                                                                                                                                                                                                                                                                                                       |
| `authorName`                                                               | `string`                             | n/a                    | Author name for comments/annotations and collaboration presence.                                                                                                                                                                                                                                                                                                |
| `collaboration`                                                            | `CollaborationConfig`                | n/a                    | Yjs real-time collaboration config (server URL, room, role).                                                                                                                                                                                                                                                                                                    |
| `shareDefaults`                                                            | `{ roomId?, userName?, serverUrl? }` | n/a                    | Seed values for the Share dialog fields.                                                                                                                                                                                                                                                                                                                        |
| `onOpenFile`                                                               | `() => void`                         | n/a                    | Host override for File > Open; bypasses the built-in file picker.                                                                                                                                                                                                                                                                                               |
| `smartArt3D`                                                               | `boolean`                            | `false`                | Opt-in Three.js 3D SmartArt renderer (needs the optional `three` peer; falls back to SVG without it).                                                                                                                                                                                                                                                           |
| `surfaceChart3D`, `barChart3D`, `lineChart3D`, `areaChart3D`, `pieChart3D` | `boolean`                            | `false`                | Independently opt in to interactive Three.js renderers for the matching 3D chart kinds; each falls back to SVG when WebGL is unavailable.                                                                                                                                                                                                                       |
| `ai`                                                                       | `PptxAiConfig`                       | n/a                    | Optional AI assistant configuration. The SDK peers load only when its panel is opened.                                                                                                                                                                                                                                                                          |
| `hiddenActions`                                                            | `ToolbarActionId[]`                  | n/a                    | Individual toolbar buttons and/or ribbon tabs to hide (e.g. `['share', 'broadcast', 'insert']`). Omit to show everything.                                                                                                                                                                                                                                       |
| `customization`                                                            | `ViewerCustomization`                | -                      | Hide, lock or remap any part of the UI (ribbon tabs/buttons, File > Options pages/sections/settings, File tab, context menus, shortcuts, panels, AI/collaboration, dialogs); the same helpers (`hideRibbonTab`, `lockSetting`, ...) are on the template ref. See the [UI Customization guide](https://christophervr.github.io/pptx-viewer/guide/customization). |
| `defaultThemeKey`                                                          | `string`                             | n/a                    | Initial File > Options > Appearance selection when no persisted preference exists.                                                                                                                                                                                                                                                                              |
| `availableThemes`                                                          | `ThemeCatalogEntry[]`                | n/a                    | Theme choices offered by File > Options > Appearance (defaults to the built-in catalog).                                                                                                                                                                                                                                                                        |
| `onThemeChange`                                                            | `(key: string) => void`              | n/a                    | Host hook for the appearance picker; when set, the host owns persisting the choice.                                                                                                                                                                                                                                                                             |
| `defaultLocale`                                                            | `string`                             | n/a                    | Initial locale code when no persisted preference exists.                                                                                                                                                                                                                                                                                                        |
| `availableLocales`                                                         | `LocaleCatalogEntry[]`               | n/a                    | Locale choices offered by File > Options > Language (defaults to the host `vue-i18n` locales).                                                                                                                                                                                                                                                                  |
| `onLocaleChange`                                                           | `(code: string) => void`             | n/a                    | Host hook for the language picker; when set, the host owns applying/persisting the switch.                                                                                                                                                                                                                                                                      |
| `accountAuth`                                                              | `AccountAuthConfig`                  | n/a                    | Optional sign-in hook point for File > Account (disabled unless `enabled: true`).                                                                                                                                                                                                                                                                               |

### Events

| Event                 | Payload               | Description                                                                          |
| --------------------- | --------------------- | ------------------------------------------------------------------------------------ |
| `active-slide-change` | `number`              | Emits the active slide index on navigation.                                          |
| `content-change`      | `Uint8Array`          | Emits updated bytes after any editing change.                                        |
| `dirty-change`        | `boolean`             | Emits `true`/`false` when the dirty state changes.                                   |
| `mode-change`         | `string`              | Emits the new mode when it changes (`'preview'`, `'edit'`, `'present'`, `'master'`). |
| `zoom-change`         | `number`              | Emits the new zoom level (1 = 100%).                                                 |
| `selection-change`    | `string[]`            | Emits the selected element IDs when selection changes.                               |
| `slide-count-change`  | `number`              | Emits the total slide count when slides are added/removed.                           |
| `autosave`            | `Uint8Array`          | Emits serialised bytes when autosave persists the presentation (`autosave` prop).    |
| `start-collaboration` | `CollaborationConfig` | Emits when the user starts a collaboration session from the Share dialog.            |
| `stop-collaboration`  | -                     | Emits when the user stops a collaboration session.                                   |

### Exposed methods (template `ref`)

| Method                    | Returns               | Description                                    |
| ------------------------- | --------------------- | ---------------------------------------------- |
| `getContent()`            | `Promise<Uint8Array>` | Serialise the current presentation to `.pptx`. |
| `goTo(index)`             | `void`                | Navigate to a slide by zero-based index.       |
| `goPrev()`                | `void`                | Navigate to the previous slide.                |
| `goNext()`                | `void`                | Navigate to the next slide.                    |
| `undo()`                  | `void`                | Undo the last editing action.                  |
| `redo()`                  | `void`                | Redo the last undone action.                   |
| `canUndo()`               | `boolean`             | Whether an undo action is available.           |
| `canRedo()`               | `boolean`             | Whether a redo action is available.            |
| `getZoom()`               | `number`              | Get the current zoom level.                    |
| `setZoom(level)`          | `void`                | Set the zoom level (clamped to 0.2 - 3.0).     |
| `zoomIn()`                | `void`                | Zoom in by one step.                           |
| `zoomOut()`               | `void`                | Zoom out by one step.                          |
| `zoomReset()`             | `void`                | Reset zoom to 100%.                            |
| `getMode()`               | `ViewerMode`          | Get the current viewer mode.                   |
| `setMode(mode)`           | `void`                | Switch mode programmatically.                  |
| `getActiveSlideIndex()`   | `number`              | Get the zero-based active slide index.         |
| `getSlideCount()`         | `number`              | Get the total number of slides.                |
| `isDirty()`               | `boolean`             | Whether the document has unsaved changes.      |
| `getSelectedElementIds()` | `string[]`            | Get IDs of currently selected elements.        |
| `selectElements(ids)`     | `void`                | Programmatically select elements by ID.        |
| `clearSelection()`        | `void`                | Clear the current selection.                   |

The exposed surface implements the full shared `PowerPointViewerAPI`, so the
following slide/element manipulation methods are also available:
`setActiveSlideIndex(index)`, `getSlides()`, `getSlide(index)`,
`getActiveSlide()`, `addSlide(afterIndex?)`, `deleteSlides(indexes)`,
`duplicateSlides(indexes)`, `moveSlide(from, to)`, `toggleHideSlides(indexes)`,
`getElements(slideIndex?)`, `getElementById(id, slideIndex?)`,
`addElement(element)`, `updateElement(id, patch)`, `deleteElements(ids)`, and
`duplicateElement(id)`.

See [element insertion](https://christophervr.github.io/pptx-viewer/vue/handle#add-element)
for the `addElement` contract and a core-factory example.

### Exported components & helpers

`PowerPointViewer`, `SlideCanvas`, `SlideStage`, `ElementRenderer`,
`RibbonToolbar`, `provideViewerTheme`, `useViewerTheme`, and the `ViewerTheme` /
`CanvasSize` / `CollaborationConfig` / `ToolbarActionId` / `RibbonProps` types.

### Composing a custom viewer shell

For host-owned collaboration, `useCollaboration` accepts a reactive `collaboration`
getter or shallow ref and exposes `shellState`: effective `canEdit`, connection
status, sanitized remote users and connected count. Use it for both custom controls
and their edit handlers. The public `/viewer` entry also exports `InlineTextEditor`
and `useInlineEditing` for reusing the native inline editing behavior.
See the [custom-shell collaboration guide](https://christophervr.github.io/pptx-viewer/vue/collaboration#custom-host-chrome)
and `demos/demo-vue/src/HostOwnedHeadlessEditor.vue` for complete wiring.

`<PowerPointViewer>` bundles the slide canvas, ribbon, inspector, and every
dialog into one component. If you only want a subset, for example your own
chrome around just the ribbon and the slide canvas, import the pieces
independently instead: `RibbonToolbar` (from `pptx-vue-viewer`, same as
`SlideCanvas`) and the `useRibbonProps` composable (from the internal
building-blocks entry point `pptx-vue-viewer/internals`) that assembles its
props. The `internals` subpath is not covered by semver; prefer the stable
root exports, and pin your version when relying on `internals`.

Most of that cross-framework logic (colour/geometry/connector/animation/chart math, slide
transitions, and more) actually lives in `pptx-viewer-shared`, an internal package that is
**never published to npm**. If you need one of those framework-neutral helpers directly, for
example the slide-transition resolver/keyframes (`resolveSlideTransition`,
`resolveTransitionDurationMs`, `SLIDE_TRANSITION_KEYFRAMES`) or the `PresentationTransitionOverlay`
component behind presentation mode, they are re-exported from `pptx-vue-viewer/internals` too, so
you never need `pptx-viewer-shared` yourself.

```vue
<script setup lang="ts">
import { SlideCanvas, RibbonToolbar } from 'pptx-vue-viewer';
import {
	useRibbonProps,
	useEditorHistory,
	useSelection,
	// ...plus whichever other composables you need to build the
	// `UseRibbonPropsInput` state/action fields (see ribbon-props-types.ts).
} from 'pptx-vue-viewer/internals';

// Wire up just the state/handlers your custom shell needs; anything from
// `UseRibbonPropsInput` you don't use can be a no-op ref/callback.
const ribbonProps = useRibbonProps({
	/* ribbonMode, canEdit, isMobile, ..., see UseRibbonPropsInput */
});
</script>

<template>
	<div class="my-custom-shell">
		<RibbonToolbar v-bind="ribbonProps" />
		<SlideCanvas :slide="activeSlide" :scale="zoom" />
	</div>
</template>
```

`RibbonToolbar`'s full prop contract is the `RibbonProps` type; `useRibbonProps`
returns a `ComputedRef<RibbonProps>` built from the same state/action
composables `PowerPointViewer.vue` itself uses, so `v-bind`ing it straight onto
`RibbonToolbar` mirrors the bundled component's wiring exactly.

## Localization (i18n)

UI labels go through [vue-i18n](https://vue-i18n.intlify.dev/) with dotted keys such as `pptx.statusBar.allSaved`. Create a `vue-i18n` instance with `createI18n()` and install it as a plugin (the demo's `src/i18n.ts` shows a minimal config, including a `missing` handler that derives Title Case labels for any key you don't explicitly translate):

```ts
import { translationsEn, keyToLabel } from 'pptx-vue-viewer/i18n';
import { createI18n } from 'vue-i18n';

const i18n = createI18n({
	legacy: false,
	locale: 'en',
	fallbackLocale: 'en',
	messages: { en: translationsEn },
	missing: (_locale, key) => keyToLabel(key),
});
```

Switch languages with `i18n.global.locale.value = 'fr'`. `pptx-vue-viewer/i18n` also exports a `TranslationKey` type for type-checking a new locale dictionary (`Record<TranslationKey, string>`) at compile time. See the [Localization guide](https://christophervr.github.io/pptx-viewer/guide/localization) for the full picture across all five viewer bindings and how to contribute a translation upstream; the live demo's language picker is a working reference.

## Limitations

A handful of effects (`backdrop-filter`, path gradients) are approximated on
screen, and a few effects flatten in raster export; see the root README's
Limitations for details.

The `pptx-viewer-core` engine parses all slide data, so anything not surfaced in
the UI is still readable from the model.

## Build (contributing)

```bash
bun run build      # Vite library build → dist (ESM + CJS + d.ts)
bun run typecheck  # vue-tsc
bun run test       # vitest
```

## Reference translations

Optional French, Spanish, German, and Simplified Chinese dictionaries ship with
this package. Import only the language you need:

```ts
import { translationsZhCN } from 'pptx-vue-viewer/i18n/zh-CN';
```

The other subpaths are `i18n/fr` (`translationsFr`), `i18n/es`
(`translationsEs`), and `i18n/de` (`translationsDe`). See the
[localization guide](https://christophervr.github.io/pptx-viewer/guide/localization)
for registration and runtime switching. Existing English imports are unchanged.

For Vue, convert the dictionary with `toVueI18nSyntax` from
`pptx-vue-viewer/i18n` before passing it to vue-i18n.

## License

[Apache-2.0](LICENSE). Please keep the [`NOTICE`](NOTICE) file with redistributions.
