npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pptx-react-viewer

v4.74.0

Published

pptx-viewer for React: a PowerPoint viewer and editor component to render, edit, and export PPTX slides in the browser.

Readme

pptx-react-viewer

npm version license types

A drop-in React component that turns a .pptx file into a fully interactive PowerPoint: view, edit, present, collaborate, and export, entirely in the browser.

Selecting, dragging, and resizing a slide element in the React demo

Slides render with real HTML/CSS (not <canvas>), so text stays crisp at any zoom, is selectable and screen-reader accessible, and every element is directly editable. The parsing/editing engine (pptx-viewer-core) is bundled in, so you install just one package.

▶️ Try the live demo · 📦 npm · 📖 Full docs · 🧩 Core SDK

Part of ooxml: Office · Word · Excel · PowerPoint · Visio · OpenTeams · Core


Install

npm install pptx-react-viewer

Then add the React peer dependencies your app uses (react / react-dom may be ^18.2 or ^19; both majors run the full test suite in CI):

npm install react react-dom framer-motion lucide-react react-icons jspdf jszip fast-xml-parser i18next react-i18next

The package is named pptx-react-viewer on npm. pptx-viewer-core (the engine) is bundled in, so you don't install it separately unless you want to call the SDK directly. Optional: three (3D models/charts, 3D SmartArt) and yjs / y-websocket / y-webrtc (real-time collaboration) are declared as optionalDependencies, so npm installs them automatically when possible; the features degrade gracefully if they're absent.

Quick start

import { PowerPointViewer } from 'pptx-react-viewer';
import { useState } from 'react';
// Not using Tailwind? Import the bundled stylesheet once at your app entry:
import 'pptx-react-viewer/styles';

export default function App() {
	const [content, setContent] = useState<Uint8Array | null>(null);

	// Load any .pptx as bytes (fetch, <input type="file">, drag-drop, …)
	const onPick = (e: React.ChangeEvent<HTMLInputElement>) =>
		e.target.files?.[0]?.arrayBuffer().then((buf) => setContent(new Uint8Array(buf)));

	return (
		<div style={{ height: '100vh' }}>
			{content ? (
				<PowerPointViewer content={content} canEdit />
			) : (
				<input type='file' accept='.pptx' onChange={onPick} />
			)}
		</div>
	);
}

The component fills its parent, so give the parent a height. That's the whole setup: open a file and you have a working viewer/editor.

To read the edited presentation back out as bytes, pass a ref and call getContent():

import { PowerPointViewer, type PowerPointViewerHandle } from 'pptx-react-viewer';
import { useRef } from 'react';

const viewerRef = useRef<PowerPointViewerHandle>(null);

// <PowerPointViewer ref={viewerRef} content={content} canEdit />
const bytes = await viewerRef.current?.getContent(); // Uint8Array of a valid .pptx

Features

| Feature | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | View | Render slides with 16 element types: shapes, text, images, tables, 23 chart types, SmartArt, connectors, media, ink, OLE, 3D models, zoom | | Edit | Insert/move/resize/delete elements, edit text inline, modify styles, manage slides | | Present | Fullscreen slideshow with 39 animation presets and 26 motion paths, 57 transitions (including morph), speaker notes, presenter view with timer | | Export | PNG/SVG/PDF/GIF/video/JSON slide export, save-as PPTX | | Collaborate | Real-time multi-user editing (powered by Yjs) with live presence, remote cursors, and user avatars | | Print | Print dialog with handout layouts and notes page formatting with overflow pagination | | Annotate | Pen/highlighter/laser pointer tools during presentations | | Find & Replace | Cross-slide text search with regex support | | Accessibility | Keyboard navigation, alt-text audit panel, screen reader support | | 3D | GLB/GLTF model rendering via Three.js, 3D surface charts, CSS 3D shape/text extrusion |


API reference

PowerPointViewer props

| Prop | Type | Default | Description | | -------------------------------------------------------------------------- | --------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | content | Uint8Array | required | Raw .pptx file bytes | | filePath | string | n/a | Optional file path (for display and autosave recovery) | | fileName | string | n/a | Display name of the open document, shown in the title bar | | className | string | n/a | Class applied to the root element | | canEdit | boolean | false | Enable editing mode | | initialSlide | number | 0 | Zero-based slide shown after each load, clamped into the deck. | | showToolbar | boolean | true | Editor chrome: title bar, ribbon, toolbar, banners and status bar. | | showThumbnails | boolean | true | Slide thumbnail pane. | | showCompatibilityToasts | boolean | true | Compatibility warning toast stack; false hides it, warnings are still collected. | | fonts | ViewerFontSource[] | n/a | Licensed font sources supplied by the host application | | 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 | | smartArt3D | boolean | false | Opt-in Three.js 3D SmartArt renderer (needs the optional three dependency; 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 | PptxAiAssistant \| PptxAiConfig | n/a | Optional AI assistant: a handle from createAiAssistant (pptx-react-viewer/ai), or a plain config. The SDK peers load only when its panel is opened. See AI assistant. | | onOpenFile | () => void | n/a | Host override for File > Open; bypasses the built-in file picker | | onContentChange | (content: Uint8Array) => void | n/a | Called with freshly serialised .pptx bytes when the content changes | | onDirtyChange | (isDirty: boolean) => void | n/a | Called when dirty state changes | | onActiveSlideChange | (index: number) => void | n/a | Called when the active slide changes | | onModeChange | (mode: ViewerMode) => void | n/a | Called when the viewer mode changes | | onZoomChange | (zoom: number) => void | n/a | Called when the zoom level changes | | onSelectionChange | (ids: string[]) => void | n/a | Called when element selection changes | | onSlideCountChange | (count: number) => void | n/a | Called when the total slide count changes | | onStartCollaboration | (config: CollaborationConfig) => void | n/a | Called when the user starts a collaboration session from the Share dialog | | onStopCollaboration | () => void | n/a | Called when the collaboration session stops | | theme | ViewerTheme | n/a | Theme configuration for customising colours, radius, and CSS vars | | hiddenActions | ToolbarActionId[] | n/a | Hide individual toolbar buttons and/or ribbon tabs (e.g. ['share', 'broadcast']) instead of the whole toolbar; omitted hides nothing | | ribbonAddIns | RibbonAddInTab[] | n/a | Ribbon tabs the host adds after Help; a command runs its run callback and raises office-ribbon-add-in (see the customization guide) | | 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 ref handle. See the UI Customization guide. | | 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. Ignored inside a PptxI18nProvider, whose locale is the language | | availableLocales | LocaleCatalogEntry[] | n/a | Locale choices offered by File > Options > Language (defaults to the registered i18next locales; inside a PptxI18nProvider no default: pass two or more with onLocaleChange) | | onLocaleChange | (code: string) => void | n/a | Host hook for the language picker; when set, the host owns applying/persisting the switch. Inside a PptxI18nProvider the host always owns it, and without this callback the Language category is left out | | accountAuth | AccountAuthConfig | n/a | Optional sign-in hook point for File > Account (disabled unless enabled: true) |

PowerPointViewerHandle (via ref)

| Method | Signature | Description | | ----------------------- | ---------------------------- | ---------------------------------------- | | getContent | () => Promise<Uint8Array> | Serialise current state to .pptx bytes | | goTo | (index: number) => 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 (1 = 100%) | | setZoom | (level: number) => void | Set the zoom level | | 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: ViewerMode) => 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 selected elements | | selectElements | (ids: string[]) => void | Programmatically select elements by ID | | clearSelection | () => void | Clear the current selection |

The handle 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 for the addElement contract, including the headless handle, and a core-factory example.

renderToCanvas

Standalone utility for rendering a DOM element to a Canvas (with an oklch colour-space workaround):

import { renderToCanvas } from 'pptx-react-viewer';

const canvas = await renderToCanvas(element, options); // => HTMLCanvasElement

Viewport fit options

<PowerPointViewer content={content} fitPadding={0} maxFitScale={null} />

fitPadding accepts a per-side CSS-pixel number or { horizontal, vertical }. maxFitScale accepts a positive ceiling or null for unlimited enlargement. Omission preserves React's 4 px horizontal / 16 px vertical padding and fit ceiling of 1. These host options do not change the document or user zoom; ruler space and surrounding chrome remain separate. See the cross-binding defaults.

Composing a custom viewer shell

<PowerPointViewer> bundles a full editor chrome (toolbar, canvas, side panels, dialogs, presentation mode). If you only need the toolbar and slide canvas, with your own layout around them, Toolbar and SlideCanvas are exported as standalone components, and useViewerBuildingBlocks wires up the same state and hooks PowerPointViewer uses internally, mapped into the flat props those two components expect:

import { Toolbar, SlideCanvas, useViewerBuildingBlocks } from 'pptx-react-viewer';

function MyCustomViewer({ content }: { content: Uint8Array }) {
	// Also returned: `mode` (current ViewerMode) and `autosaveStatus`
	const { toolbarProps, canvasProps, loading, error } = useViewerBuildingBlocks({
		content,
		canEdit: true,
		fitPadding: 0,
		maxFitScale: null,
	});

	if (loading) return <p>Loading…</p>;
	if (error) return <p>Failed to load: {error}</p>;

	return (
		<div style={{ display: 'flex', flexDirection: 'column', height: 540, minHeight: 0 }}>
			<Toolbar {...toolbarProps} />
			<SlideCanvas {...canvasProps} showRulers={false} />
		</div>
	);
}

The example disables rulers for edge-aligned fitting. Existing ruler offsets are unchanged when rulers are enabled; fitPadding: 0 does not remove those offsets. The toolbar also uses part of the host height, so the canvas fits its remaining viewport rather than the whole 540 px host.

useViewerBuildingBlocks accepts the same content / canEdit / filePath / hiddenActions / onDirtyChange-style inputs as PowerPointViewer, plus onOpenSettings / onOpenHeaderFooter / onOpenShareDialog callbacks (fired by the corresponding toolbar buttons) since this composition doesn't render those dialogs itself. It's an additive alternative, not a replacement: dialogs, presentation-mode overlays, mobile chrome, resizable side panels, and real-time collaboration are all part of PowerPointViewer and are out of scope for these building blocks. Reach for PowerPointViewer when you need the full editor; reach for useViewerBuildingBlocks when you're assembling your own chrome around just the toolbar and canvas.

The bundled stylesheet's base reset (Tailwind's preflight) applies only inside the viewer root, a canvas viewport and elements with a data-pptx-scope attribute, so your page's own headings, lists and margins stay as they are. SlideCanvas and Toolbar carry the scope themselves; give any other element of a composed shell that uses the viewer's classes an ancestor with data-pptx-scope. A canvas-only shell can import pptx-react-viewer/canvas.css instead of pptx-react-viewer/styles.


Styling & theming

The viewer's UI references CSS custom properties (--pptx-*, the shadcn/ui token convention) for every visual token, so it works three ways:

  • Tailwind CSS v4 project: the viewer classes resolve through your existing Tailwind tokens. No extra CSS import needed.
  • No Tailwind: import the bundled stylesheet once at your app entry: import 'pptx-react-viewer/styles';
  • Canvas only: a host that renders just SlideCanvas (see Composing a custom viewer shell) can import pptx-react-viewer/canvas.css instead: the same tokens and reset with only the utilities the canvas uses (about 46 KB, 8 KB gzipped, against 112 KB, 16 KB gzipped).
  • CSS custom properties: define the --pptx-* properties yourself for full control.

Override specific values with the theme prop:

<PowerPointViewer
	content={bytes}
	theme={{
		colors: { primary: '#6366f1', background: '#0f172a' },
		radius: '0.5rem',
	}}
/>

All ViewerTheme.colors keys are optional; override only what you need. Helpers defaultThemeColors, defaultRadius, themeToCssVars, defaultCssVars, ViewerThemeProvider, and useViewerTheme are exported for advanced use. See the full docs for the complete token list.

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:

import { PowerPointViewer, vermilionLightTheme } from 'pptx-react-viewer';

<PowerPointViewer content={bytes} theme={vermilionLightTheme} />;

The underlying palettes (vermilionLightColors, vermilionDarkColors) and radius (vermilionRadius) are exported too, so you can spread them into your own variant.

AI assistant

Create the assistant with createAiAssistant from the pptx-react-viewer/ai entry. Install the ai and @ai-sdk/react peers, create an assistant once (at module scope or in useMemo) and pass it to the ai prop:

import { PowerPointViewer } from 'pptx-react-viewer';
import { createAiAssistant } from 'pptx-react-viewer/ai';

const assistant = createAiAssistant({ connection: { kind: 'endpoint', api: '/api/ai/chat' } });

<PowerPointViewer content={bytes} ai={assistant} />;

The panel still loads lazily, the first time the user opens it, and the main entries still reach the same lazy panel loader. The handle is opaque: only createAiAssistant makes one, and any other value is read as a plain config. A plain PptxAiConfig (ai={{ connection }}) keeps working through the viewer's own lazy panel and is not deprecated in this release; the handle is a preparatory, opt-in path for a later major release (see The /ai entry). The PptxAi* types are exported from both pptx-react-viewer and pptx-react-viewer/ai. See the AI assistant guide for connection modes, tools and the write policy.

Localization (i18n)

UI labels go through i18next / react-i18next with dotted keys such as pptx.statusBar.allSaved, or through your own i18n library with PptxI18nProvider (see below). Initialise an i18next instance and wrap your app in I18nextProvider (the demo's demos/pptx/demo-react/i18n.ts shows a minimal config, including a parseMissingKeyHandler that derives Title Case labels for any key you don't explicitly translate). pptx-react-viewer/i18n exports translationsEn (the English dictionary), keyToLabel (the fallback), and a TranslationKey type you can use to type-check a new locale dictionary (Record<TranslationKey, string>) at compile time. Add a new language by supplying a resource bundle under its language code. See the Localization guide for full wiring examples and how to contribute a translation upstream; the live demo's language picker is a working reference.

Use your own i18n library

To translate the viewer with Lingui, FormatJS or any other library, wrap it (or the standalone building blocks) in PptxI18nProvider and pass a translate(key, params) function plus the active locale:

import { i18n } from '@lingui/core';
import { useLingui } from '@lingui/react';
import { LOCALE_CATALOG, PowerPointViewer, PptxI18nProvider } from 'pptx-react-viewer';
import type { PptxTranslate } from 'pptx-react-viewer';

import { dynamicActivate } from './i18n'; // loads a catalog, then calls i18n.activate(code)

// Module level, so it never changes: the viewer re-translates when `locale` changes.
const translate: PptxTranslate = (key, params) => i18n._(key, params);
const LOCALES = LOCALE_CATALOG.filter((entry) => ['en', 'fr'].includes(entry.code));

function TranslatedViewer({ content }: { content: Uint8Array }) {
	const { i18n: active } = useLingui(); // re-renders when the language changes
	return (
		<PptxI18nProvider locale={active.locale} translate={translate}>
			<PowerPointViewer
				content={content}
				availableLocales={LOCALES}
				onLocaleChange={(code) => void dynamicActivate(code)}
			/>
		</PptxI18nProvider>
	);
}

translate receives the viewer's params unchanged: defaultValue, count and the values of the {{name}} placeholders. Use the same names in your catalog ({name} in ICU syntax); count is a plain value, so pluralise in your catalog. {{name}} placeholders left in the text you return are filled too, so the shipped dictionaries work as they are: const translate: PptxTranslate = (key) => translationsFr[key];. A miss (the key itself, an empty string, nothing, or a throw) shows the built-in English text, then the call's default text, then a label derived from the key, never a raw key. Keep translate stable (a module-level function or useCallback): the viewer re-translates whenever translate or locale changes.

Inside a provider the host owns the language: the viewer never calls changeLanguage, ignores defaultLocale and never restores a stored language choice. File > Options > Language appears only with onLocaleChange and two or more availableLocales; a pick calls onLocaleChange, and the selection follows locale (fr-CA selects an offered fr). A PowerPointViewer reads the provider nearest above it; a provider rendered inside the viewer is ignored. i18next and react-i18next stay required peer dependencies in 4.x: install them, but inside a provider the viewer does not use them. PptxI18nProvider is React-only: the Vue and Angular bindings delegate to vue-i18n and ngx-translate, and Vanilla and Svelte take messages dictionaries.

How it's built

You only need the <PowerPointViewer> component; everything else is internal. Behind it, the logic lives in many small, focused React hooks, and the components themselves just draw what those hooks produce. Slides are rendered as ordinary HTML and CSS (charts as inline SVG, tables as real <table> elements), which is why text stays sharp, selectable, and accessible. For the full component tree, the rendering pipeline, the animation and transition engine, connector routing, collaboration, and a file-by-file map, see the full documentation.

A small curated set of those hooks is exported from pptx-react-viewer/viewer with a stable API; the complete set (80+) is also importable from pptx-react-viewer/internals for advanced integrations. The internals subpath is not covered by semver: prefer the stable root exports. See the Hooks reference for the full list.

The bulk of that cross-framework logic (colour/geometry/connector/animation/chart math, slide transitions, and more) actually lives in pptx-viewer-shared, a private facade over the shared renderer in ooxml-ui/pptx, 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-react-viewer/internals too, so you never need pptx-viewer-shared yourself.

Limitations

CSS-based rendering trades a few visual effects for crisp text, accessibility, and DOM interactivity: backdrop-filter becomes semi-transparent backgrounds and path gradients approximate as elliptical radials, while mix-blend-mode and CSS 3D transforms render natively on screen but flatten in raster export. Text uses fonts available in the browser (embedded fonts are injected when present). Media playback depends on browser codec support. SmartArt is decomposed into editable shapes with a live reflow engine for structural edits. Charts are editable directly on the canvas (hover a mark for its tooltip, click to select, drag it to a new value, double-click the title to rename), except for stacked/percent-stacked, pie, radar, surface and map kinds, which are click-to-select and edited in the inspector data grid. 3D models need the optional Three.js peer. See the full docs for the complete list.

Reference translations

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

import { translationsZhCN } from 'pptx-react-viewer/i18n/zh-CN';

The other subpaths are i18n/fr (translationsFr), i18n/es (translationsEs), and i18n/de (translationsDe). See the localization guide for registration and runtime switching. Existing English imports are unchanged.

Privacy and network use

The viewer renders and saves decks in the browser and never uploads them. It contacts other servers only in these cases:

  • Fonts that are neither installed nor embedded. When a deck uses such a font and Google Fonts serves it (or a metric-compatible clone, such as Carlito for Calibri), the viewer downloads it from fonts.googleapis.com and fonts.gstatic.com, so the reader's IP address and the needed family names reach Google. This is on by default, as in PowerPoint, which downloads its cloud fonts. Turn it off with googleFonts: false, or send the same requests to a self-hosted mirror with googleFonts: { baseUrl: 'https://fonts.example.com/css2' }. Readers can also turn it off in File > Options > Trust Center ("Download fonts that are not installed or embedded (Google Fonts)"); a host value locks that toggle. With downloads off, text uses installed, embedded or substitute fonts and line breaks can differ from PowerPoint. If the download is slow, the deck waits up to 8 seconds, and it opens at once when the font server cannot be reached (offline use works). See font loading.
  • Linked pictures and media in a deck are fetched from their URLs while the Trust Center option "Allow external content" is on (the default). allowExternalContent: false blocks them.
  • Share and Broadcast, only when started: updates go to the server you configure; with no server, peers meet through y-webrtc's public signaling server and public STUN servers.

Nothing collects analytics or telemetry. The full list, including what is kept in local storage, is in PRIVACY.md.

License

Apache-2.0. Please keep the NOTICE file with redistributions.

OneDrive integration

Use the optional shared ooxml-core/embedding/onedrive connection with host-supplied Microsoft sign-in. See the Office package and integration reference for this product's load/save API, native storage wiring, sharing permissions and conflict recovery. The testing guide separates test-local transports from live Microsoft acceptance. No simulation service is shipped; OneDrive storage does not provide Microsoft Office coauthoring or automatic document merging.