@runstamp/sdk/react is the SSR-safe layer for Runstamp decks. Its browser exports have no Node.js imports. The file renderer is in the @runstamp/sdk/react/server entry point.
Install
pnpm add @runstamp/sdk react react-domImport the default stylesheet once, in your app's entry point:
import "@runstamp/sdk/react/styles.css";Complete viewer
This Client Component renders a controlled viewer, a thumbnail rail, a navigation and download toolbar, and the fidelity state.
"use client";
import {
DeckProvider,
DeckThumbnails,
DeckToolbar,
DeckViewer,
useDeck,
type DeclarativeDocument,
} from "@runstamp/sdk/react";
import "@runstamp/sdk/react/styles.css";
const document: DeclarativeDocument = {
title: "Q3 operating review",
slides: [
{
layout: "title",
title: "Momentum with a clearer path to scale",
subtitle: "Q3 operating review",
},
{
layout: "kpi-row",
title: "The business at a glance",
metrics: [
{ label: "ARR", value: "$8.4M", delta: "+31% YoY", trend: "up" },
{ label: "NRR", value: "118%", delta: "+6 pts", trend: "up" },
],
},
],
};
export default function DeckWorkspace() {
const deck = useDeck(document);
return (
<DeckProvider deck={deck}>
<div className="deck-workspace">
<DeckThumbnails deck={deck} ariaLabel="Review slides" />
<main>
<DeckToolbar
deck={deck}
fileName="q3-operating-review"
pptx="/api/decks/q3-operating-review.pptx"
fidelity={{ status: "unverified", message: "Not yet Office validated" }}
/>
<DeckViewer deck={deck} ariaLabel="Q3 operating review" />
</main>
</div>
</DeckProvider>
);
}The pptx and pdf props accept a URL, Blob, Uint8Array or an async function that returns one. A button is disabled when its source is omitted.
DeckProvider
Gives headless descendants one DeckController. Its deck prop takes a DeckDocument or an existing controller. Components can take deck directly or resolve it from the nearest provider.
DeckViewer
Renders the active slide on a responsive 16:9 canvas.
| Prop | Type | Default |
|---|---|---|
deck | DeckDocument | DeckController | Nearest provider |
theme | "light" | "dark" | DeckTheme | "light" |
renderSlide | (state: DeckRenderState) => ReactNode | Built-in renderer |
ariaLabel | string | "Presentation slide" |
className, style | React styling props | — |
DeckThumbnails
Renders each slide as a selectable thumbnail. renderThumbnail(state) replaces the built-in miniature and keeps navigation and accessible labels.
DeckToolbar
Renders previous and next navigation, the zero-padded page position, optional PPTX and PDF downloads, and an optional fidelity result.
| Prop | Type | Meaning |
|---|---|---|
fileName | string | Base download name; defaults to presentation. |
pptx, pdf | DownloadSource | URL, bytes, blob, or async producer. |
fidelity | FidelityStatus | FidelityResult | Real validation result shown beside downloads. |
FidelityBadge
import { FidelityBadge } from "@runstamp/sdk/react";
export default function ValidationStatus() {
return (
<FidelityBadge
status={{
status: "passed",
platform: "PowerPoint for Windows",
validatedAt: "2026-08-10T00:00:00.000Z",
}}
/>
);
}Statuses: passed, failed, pending and unverified. Use passed only for a real validation result. The badge sets role="status". Pending results also use a polite live region.
useDeck(source?, options?)
Creates or reads a DeckController. initialSlide is zero-based and clamped to the document bounds.
The controller exposes document, currentSlide, slideCount, canPrevious, canNext, setCurrentSlide(index), previous(), next(), first(), and last().
useDeckRender(source?, slideIndex?)
Returns a DeckRenderState for the active or requested slide, with no markup:
document,slide,slideIndex, andslideCount- the resolved slide
title modelwith normalized prose, bullets, KPIs, chart data, or AST nodes
Use it to write your own markup and keep Runstamp navigation state:
import { DeckProvider, useDeckRender, type DeclarativeDocument } from "@runstamp/sdk/react";
const document: DeclarativeDocument = {
title: "Headless example",
slides: [{ layout: "title", title: "Headless Runstamp" }],
};
function CurrentSlide() {
const { title, slideIndex, slideCount } = useDeckRender();
return <h1>{title} — {slideIndex + 1} / {slideCount}</h1>;
}
export default function HeadlessDeck() {
return <DeckProvider deck={document}><CurrentSlide /></DeckProvider>;
}Calling either hook without a source or a parent provider throws an actionable error.
Node server renderer
Do not import the server entry point into a Client Component. It wraps the native engine for server routes and scripts.
import { writeFile } from "node:fs/promises";
import type { DeclarativeDocument } from "@runstamp/sdk/react";
import { createRunstampRenderer } from "@runstamp/sdk/react/server";
const renderer = createRunstampRenderer();
const document: DeclarativeDocument = {
title: "Server-rendered deck",
slides: [{ layout: "title", title: "Generated on the server" }],
};
const buffer = await renderer.renderPptx(document);
await writeFile("server-rendered.pptx", buffer);The adapter also exposes renderPdf(document, options?). renderDeckToPptx(document, options?) and renderDeckToPdf(document, options?) each run one render with the default adapter.
In a Next.js Route Handler, export runtime = "nodejs", return the bytes directly, and set the PPTX content type documented in Troubleshooting.