Hooks
Connect React controls to PPTX editor state and commands with hooks.
Call editor-state hooks inside PptxEditor.Root. The packaged <PptxEditor> provides that root for its children.
usePptxSource and useFonts prepare input for the root, so you can call them in the parent component.
Connect a command button
This button runs a toolbar command and shows why the command cannot run:
import { useEditorCommand, useTranslation } from '@pptx-editor.dev/react';
import { translateKeyOrText } from '@pptx-editor.dev/core/editor';
export function BoldButton() {
const bold = useEditorCommand('text.bold');
const { t } = useTranslation();
const reason = bold.disabledReason;
return (
<button
type="button"
aria-disabled={!bold.isEnabled || undefined}
aria-pressed={bold.isActive}
aria-label={t('toolbar.bold')}
title={reason ? translateKeyOrText(t, reason) : t('toolbar.bold')}
onMouseDown={(event) => event.preventDefault()}
onClick={() => bold.execute()}
>
B
</button>
);
}useEditorCommand(slot: ChromeSlotId): UseEditorCommandReturn takes a slot identifier from the toolbar slot reference.
| Member | Type | Description |
|---|---|---|
execute(value?) | boolean | Runs the slot's command, with a value for value slots. Returns true when the engine ran it, and false on a refusal. |
isEnabled | boolean | Whether the command can run now. |
isActive | boolean | Whether a toggle control shows as pressed. |
disabledReason | string | null | A translation key, or the engine's own text, when the command cannot run. |
value | string | number | undefined | The value a value slot shows, such as a font size or a family. |
The types in these signatures, such as ChromeSlotId, CommandResult, EditorSnapshot, and PptxEditorInstance, are exported from @pptx-editor.dev/react. You do not need an import from @pptx-editor.dev/core to type your handlers.
Read state
useEditorState(selector, isEqual?) returns one slice of the editor snapshot. The component renders again only when the slice changes:
import { useEditorState } from '@pptx-editor.dev/react';
export function SlideCounter() {
const current = useEditorState((s) => s.currentSlide);
const total = useEditorState((s) => s.slides.length);
return (
<span>
{current + 1} / {total}
</span>
);
}Pass a comparator for an object slice. Common fields include status, slides, currentSlide, selection, format, mode, canUndo, canRedo, presentationView, and lastRejection.
| Hook | Returns | Notes |
|---|---|---|
useEditorState(selector, isEqual?) | T | The selected snapshot value. isEqual defaults to Object.is. |
usePptxEditor() | PptxEditorInstance | null | null before the editor mounts and outside a root. |
useEditorColorMode() | { colorMode: ResolvedColorMode } | 'light' or 'dark', after 'system' resolves. |
useTranslation() | { t: Translate } | The translator for the root's locale. See Language. |
usePptxEditor
Use the instance for actions and one-time reads:
import { usePptxEditor } from '@pptx-editor.dev/react';
export function SaveButton({ upload }: { upload: (bytes: Uint8Array) => Promise<void> }) {
const editor = usePptxEditor();
return (
<button
disabled={!editor}
onClick={() => {
const saved = editor?.save();
if (saved) void upload(saved);
}}
>
Save
</button>
);
}A direct snapshot() call does not subscribe the component. Use useEditorState for values that you render.
Choose a hook
Formatting
| Hook | Returns |
|---|---|
useFontFormat() | format: the font of the selection, or null. isEnabled. apply(update) writes the fields as one undo step. |
useParagraphFormat() | format: the paragraph formatting of the selection, or null. isEnabled. apply(update) writes the fields as one undo step. |
useBulletFormat() | format: the bullet format, null for no list, or undefined when the paragraphs differ. isEnabled. apply(format) writes it, and apply(null) turns the list off. |
useEditorValueCommand(slot) | A typed value slot. See Apply a value. |
Slides and panes
| Hook | Returns |
|---|---|
useSlideLayouts() | The deck's slide layouts (SlideLayoutSummary[]), in gallery order. See Build a layout picker. |
useNotesPane() | open, setOpen(open), and toggle() for the notes pane of the root. |
useSlideshow() | controller (or null while no show runs), start('start' | 'current'), and stop(). |
Dialogs, menus, and notices
| Hook | Returns |
|---|---|
useEditorDialog(key) | isOpen, isAvailable, open(returnFocusTo?), and close() for a dialog of the popups registry. See Open a dialog from your control. |
usePageSetupDialog(), useBackgroundDialog(), useParagraphDialog(), useFontDialog(), useBulletsAndNumberingDialog(), useColumnsDialog(), useImageAltTextDialog(), useImagePropertiesDialog() | The draft of an open dialog: values, errors, isEnabled, setValue(name, value), apply(), and cancel(). useBackgroundDialog() adds reset(). Call them inside the dialog. See Dialog parts and draft state. |
useContextMenuRequest() | The open canvas context menu request, or null. |
useContextMenuTarget() | The element the open context menu opened on, or null. |
useOpenFailure({ t? }) | failure, message, kept, and dismiss() for a file that did not open. t translates message. See Show a banner or a toast. |
useHyperlinkPopup(), useHyperlinkPopupInstance(active?) | The link popup's state and actions, with no UI. See Build your own link panel. |
Fonts
| Hook | Returns |
|---|---|
useFonts(source, ...fragments) or useFonts(...origins) | A font resolver with a stable identity, for the fonts prop. See Fonts. |
useFontFamily() | UseFontFamilyReturn: value, theme, options, groups, isCurrent(row), setValue(family), isEnabled, and disabledReason. Use it for your own font picker. See Build the list from useFontFamily. |
Open a presentation
usePptxSource(source, { fonts?, fetchOptions? }) loads bytes from a URL and resolves fonts. It cancels stale fetches when the source changes or the component unmounts:
import { PptxEditor, usePptxSource } from '@pptx-editor.dev/react';
import { packagedFonts } from '@docx-editor.dev/fonts';
export function Deck({ url }: { url: string }) {
const { document, fonts, error } = usePptxSource(url, { fonts: packagedFonts() });
if (error) return <p>{error.message}</p>;
return <PptxEditor document={document} fonts={fonts} />;
}| Member | Type | Description |
|---|---|---|
document | Uint8Array | undefined | The bytes, for the document prop. |
fonts | FontConfiguration | FontResolver | undefined | The value for the fonts prop. |
error | Error | null | Why the presentation did not open. A font failure never shows here. |
isLoading | boolean | True until the presentation arrives or fails, and for up to 2 seconds while eager fonts load. |
Eager fonts (a value, a promise, or a zero-argument loader) hold the deck back until they settle, so the slides lay out once. They hold it for at most 2 seconds. Fonts that settle later still apply, and the slides lay out again. An on-demand resolver (packagedFonts(), googleFonts()) needs the parsed deck, so the bytes go through at once, and fonts is one resolver for the life of the hook.
A string source is always a URL, fetched as you wrote it. Check a URL from user input first. When no bytes arrive for 10 seconds, the download stops and error is a TimeoutError. A slow download that keeps sending bytes finishes.
The download also stops when the file is longer than 528 MiB, the longest file the editor opens. The hook checks the Content-Length header before the body arrives, and the byte count while it downloads. In both cases, error is a RangeError.
Build your own chrome
Use these hooks to connect your controls to the editor.
Zoom
useZoom(): UseZoomResult reads and sets the slide zoom. This control replaces the zoom pill:
import { useZoom } from '@pptx-editor.dev/react';
export function ZoomControl() {
const { zoom, isFit, fit, zoomIn, zoomOut, canZoomIn, canZoomOut } = useZoom();
return (
<div role="group" aria-label="Zoom">
<button onClick={zoomOut} disabled={!canZoomOut} aria-label="Zoom out">
-
</button>
<span>{Math.round(zoom * 100)}%</span>
<button onClick={zoomIn} disabled={!canZoomIn} aria-label="Zoom in">
+
</button>
<button onClick={fit} aria-pressed={isFit}>
Fit
</button>
</div>
);
}| Member | Type | Description |
|---|---|---|
zoom | number | The zoom in force. 1 is 100 %. In fit mode, it is the zoom that fits the slide. |
isFit | boolean | Whether the slide fits the window and follows its size. |
setZoom(zoom) | void | Sets a fixed zoom from 10 % to 400 %, and leaves fit mode. |
fit() | void | Fits the slide to the window. |
reset() | void | Sets a fixed 100 %. |
zoomIn(), zoomOut() | void | Move one step of levels from the zoom in force. |
canZoomIn, canZoomOut | boolean | Whether a step is possible. |
levels | readonly number[] | The steps that the zoom buttons use. |
Use fit() to fit the whole slide. Use setZoom() for a fixed zoom level.
Events
useEditorEvent(event, handler): void calls handler each time the event occurs, while the component is mounted:
import { useEditorEvent } from '@pptx-editor.dev/react';
export function RefusalToast({ show }: { show: (text: string) => void }) {
useEditorEvent('rejection', (rejection) => show(rejection.reason));
return null;
}| Event | Handler | Occurs when |
|---|---|---|
change | (snapshot) => void | An edit applies, an undo or redo runs, or a load finishes or fails. |
selectionChange | (snapshot) => void | The shape or text selection, the current slide, or the selected slides change. |
rejection | (rejection, snapshot) => void | The editor refuses an edit. |
openFailure | (failure, snapshot) => void | A file does not open. |
The events come from the editor's one subscription. Outside the adapters, onEditorEvent(editor, event, handler) from @pptx-editor.dev/core/editor gives the same events and returns the unsubscribe function. The PptxEditorEvents type lists the events.
Other hooks
| Hook | Returns | Notes |
|---|---|---|
useEditorSnapshot(editor) | number | A counter that changes on each editor change. Prefer useEditorState, which renders again only when its slice changes. |
useScopeClassName() | '' | 'pptx-editor ' | 'pptx-editor dark ' | The scope classes for the root element of your own part, so the --doc-* tokens apply. See Style your controls. |
useEditorHostDocument() | Document | null | The document that the slide is mounted in: an iframe's when you mount the editor there, else the page's. Use it in place of the global document for listeners and focus. null during server rendering. |
Find, caret, and undo steps
| Hook | Returns | Notes |
|---|---|---|
useDocumentSearch() | UseDocumentSearchResult | A find session with no UI, for your own find bar: query, setQuery, matchCase, wholeWord, matches, activeIndex, activeMatch, goTo(index), next(), previous(), find(query, { selectFirst }), and clear(). Every hook on one editor shares the session. |
useEditorCaret() | EditorCaret | null | The caret while text is edited: nodeId, paragraph, offset, and cell in a table. It changes only when the caret moves. |
useHistoryGroup({ kind }) | UseHistoryGroupReturn | One undo step for one gesture on your own control. Put ref on the control, and pass options() to each command of the gesture. kind is range, repeat, keyboard, or native-color. |
import { useEditorValueCommand, useHistoryGroup } from '@pptx-editor.dev/react';
export function SizeSlider() {
const group = useHistoryGroup({ kind: 'range' });
const size = useEditorValueCommand('font.size');
return (
<input
type="range"
aria-label="Font size"
min={8}
max={96}
ref={group.ref}
onChange={(e) => size.execute(Number(e.target.value), group.options())}
/>
);
}Labels
| Hook | Returns | Notes |
|---|---|---|
useChromeTranslate(overrides?) | ChromeTranslate | The t of the nearest compound, else the root's locale. overrides is a Map that wins for its keys. |
useToolbarContext() | ToolbarContextValue | { t }: the host's t on the nearest compound, or undefined. |
useToolbarLabel() | (key) => string | The label for a key: the compound's t, else the locale catalog. |
useToolbarLabelFor(t) | (key) => string | The same, for a resolver that you pass. |
Toolbar, Menu, ContextMenu, SlideRail, StatusBar, FindBar, OpenFailure, FontNotice, and EditNotice take a t prop. With it, the compound and its parts take every label from your function. The dialogs take their labels from the root's locale.
Search selection
Find shows the active match as the slide selection. useDocumentSearch does not expose a named text highlight layer.
Apply a value
useEditorValueCommand(slot) reads the value of a value slot and applies a new one, with the slot's type from ToolbarValueMap. This slider sets the font size:
import { useEditorValueCommand } from '@pptx-editor.dev/react';
export function FontSizeSlider() {
const size = useEditorValueCommand('font.size');
return (
<label>
{size.value === null ? 'Mixed' : `${size.value} pt`}
<input
type="range"
min={8}
max={96}
value={size.value ?? 18}
disabled={!size.isEnabled}
onChange={(event) => {
const result = size.execute(Number(event.target.value));
if (!result.ok) console.info(result.reason);
}}
/>
</label>
);
}font.size takes points, or { step: 1 } and { step: -1 } for one step of the size list. null means mixed or unavailable.
| Member | Type | Description |
|---|---|---|
execute(value, options?) | CommandResult | Runs the command. A refusal gives ok: false and the reason, such as disabled.invalidValue. |
value | S | null | The value the slot shows, or null when mixed or unavailable. |
options | readonly V[] | The slot's fixed choices. A slot with a free value has none. |
isEnabled, disabledReason | boolean, string | null | As useEditorCommand gives them. |
The two hooks return different results from execute: useEditorCommand returns a boolean, because a toolbar button only needs to know if the command ran, and useEditorValueCommand returns the CommandResult, because a value control shows why the engine refused a value.
To make the values of one slider drag one undo step, pass { historyGroup } from editor.beginHistoryGroup(). See Group a slider or a stepper.
Limits
- Call hooks inside
PptxEditor.Root. Outside it,usePptxEditor()returnsnull. canUndoreports undo availability, not unsaved changes. Track saves yourself.- Dialog draft hooks read a draft. They do not open the dialog; use
useEditorDialog(key)for that. disabledReasoncan be a translation key. Show it withtranslateKeyOrText(t, reason)from@pptx-editor.dev/core/editor.