presentx
v2.0.0
Published
HTML slide presentation generator with PPTX export, self-healing validation, brand injection, and streaming React viewer
Downloads
90
Maintainers
Readme
presentx
HTML slide presentation generator with PPTX export, self-healing validation, brand injection, and a streaming React viewer.
Generate slide decks from HTML, preview them live with streaming support, and export to editable PowerPoint files -- all client-side.
Features
- HTML to PPTX -- Convert HTML slides to editable PowerPoint files with native charts, preserved layouts, and editable text
- Self-healing validator -- Automatically fixes missing dimensions, overflow, flex layout, chart types, and other common LLM output issues
- Streaming React viewer -- Progressive rendering with skeleton loading, slide counter, and auto-scroll during generation
- Brand injection -- Automatically inject logos, headers, footers, and copyright into every slide via theme configuration
- Customizable themes -- Register themes with colors, fonts, chart palettes, table styles, and brand assets
- LLM prompt generation -- Theme-aware system prompt fragments for teaching LLMs to create slide presentations
- Native Chart.js support -- Chart.js data in slides is converted to native, editable PowerPoint charts
- Material Icons -- Automatic rasterization of Material Icons for PPTX compatibility
Installation
npm install presentxQuick Start
React Viewer
import { SlideViewer } from 'presentx/react';
import 'presentx/styles';
function App() {
return (
<SlideViewer
html={slidesHtml}
isStreaming={false}
theme="default"
/>
);
}Export to PPTX
import { convertSlidesToPptx, validatePresentation } from 'presentx';
// Validate and self-heal the HTML
const report = validatePresentation(slidesHtml);
// Convert to PowerPoint
const blob = await convertSlidesToPptx(report.fixedHtml, 'My Presentation');
// Download
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'presentation.pptx';
a.click();LLM Prompt Generation (Node.js)
const { getSlideInstructions } = require('presentx/prompts');
const prompt = getSlideInstructions({ theme: 'my-brand' });
// Returns a system prompt fragment that teaches the LLM how to create slidesEntry Points
| Import | Environment | Contents |
|--------|-------------|----------|
| presentx | Browser | Converter, validator, themes, brand injection, utilities |
| presentx/react | Browser + React | SlideViewer, SlideCounter components |
| presentx/prompts | Node.js / Browser | LLM instruction generation, routing rules, theme registry |
| presentx/styles | Browser | CSS for the React viewer |
Integrating with LLM / Chat Platforms
presentx is designed to work with any LLM-powered chat platform. The library provides system prompt fragments that teach models how to generate slide HTML, a streaming viewer for real-time preview, and a PPTX converter for download -- all decoupled so you can adopt them independently.
Step 1: Inject the System Prompt (Server-Side)
On your API server, use presentx/prompts to generate a system prompt fragment. This teaches the LLM the slide HTML format, available components (charts, tables, icons, images), layout rules, and your brand palette.
// server/prompts.js
const { getSlideInstructions, registerTheme } = require('presentx/prompts');
// Optional: register a custom brand theme
registerTheme({
name: 'my-brand',
colors: {
primary: '#1a1a2e',
accent: '#e94560',
text: '#333333',
textLight: '#888888',
bgDark: '#1a1a2e',
bgLight: '#f5f5f5',
},
fonts: { heading: 'Montserrat', body: 'Inter' },
chartPalette: ['#1a1a2e', '#e94560', '#0f3460', '#16213e', '#533483'],
});
// Generate the slide instructions
const slidePrompt = getSlideInstructions({
theme: 'my-brand',
// Optional: add platform-specific instructions on top
extraInstructions: '## Platform Rules:\n- Use websearch for images\n- No red in charts',
});
// Append to your system prompt
const systemPrompt = baseSystemPrompt + '\n\n' + slidePrompt;The prompt includes:
- Slide frame templates (960x540px body slides, title/closing slides)
- Font scale, layout patterns (KPIs, timelines, grids, charts)
- Chart.js
<canvas>+<script class="chart-data">syntax - Table styling with inline styles for PPTX compatibility
- Brand palette, fonts, and branding notices from your theme
- Hard rules (no truncation, complete tags, HTTPS images, etc.)
Step 2: Detect and Render Slides (Client-Side)
When the LLM responds with slide content (using your artifact syntax or a marker), render it with the SlideViewer:
// client/SlideArtifact.tsx
import { SlideViewer } from 'presentx/react';
import { registerTheme } from 'presentx';
import 'presentx/styles';
// Register the same theme on the client (for brand injection in the viewer)
registerTheme({
name: 'my-brand',
colors: { /* same as server */ },
fonts: { /* same as server */ },
chartPalette: [ /* same as server */ ],
brand: {
logo: '<svg>...</svg>', // Inline SVG or image URL
logoDark: '<svg>...</svg>', // Optional: for dark slide backgrounds
logoIcon: '<svg>...</svg>', // Optional: small icon for body slide headers
name: 'My Company',
header: {
enabled: true,
content: 'icon', // 'logo' | 'icon' | 'name'
position: 'right',
height: 36,
},
footer: {
enabled: true,
showLogo: true,
logoPosition: 'right',
copyright: '© 2025 My Company',
copyrightPosition: 'left',
height: 32,
},
},
});
function SlideArtifact({ html, isStreaming }: { html: string; isStreaming: boolean }) {
return (
<SlideViewer
html={html}
isStreaming={isStreaming}
theme="my-brand"
/>
);
}The viewer handles:
- Streaming: progressively renders slides as the LLM generates HTML tokens
- Validation: auto-fixes malformed slides (missing dimensions, broken tags)
- Brand injection: overlays logo/header/footer from the theme config
- Iframe sandbox: slides render in a sandboxed iframe with Tailwind, Google Fonts, Material Icons, and Chart.js pre-loaded
Step 3: PPTX Download (Client-Side)
Add a download button that converts the viewed slides to an editable PowerPoint file:
import { convertSlidesToPptx, validatePresentation, getTheme, injectBrand } from 'presentx';
async function downloadPptx(html: string, title: string) {
// 1. Validate and self-heal
const report = validatePresentation(html);
// 2. Inject brand elements (logo, header, footer)
const theme = getTheme('my-brand');
const branded = injectBrand(report.fixedHtml, theme);
// 3. Convert to PPTX
const blob = await convertSlidesToPptx(branded, title, { theme: 'my-brand' });
// 4. Trigger download
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${title}.pptx`;
a.click();
URL.revokeObjectURL(url);
}The PPTX converter:
- Renders slides in a hidden iframe to capture exact layout via
getBoundingClientRect() - Converts Chart.js canvases to native editable PPTX charts
- Rasterizes SVG logos and Material Icons to embedded PNGs
- Embeds fonts for cross-platform fidelity
- Preserves table styles, borders, and zebra striping
Architecture Overview
LLM generates HTML slides
|
v
getSlideInstructions() <-- Server: teaches LLM the slide format
|
v
validatePresentation() <-- Client: self-heals malformed output
|
v
injectBrand() <-- Client: adds logo/header/footer from theme
|
+---> SlideViewer <-- Client: live preview (streaming or static)
|
+---> convertSlidesToPptx() <-- Client: export to editable PowerPoint
|
v
PptxGenJS engine <-- DOM traversal + native charts + font embeddingThemes
Built-in Theme
default-- Neutral palette (navy primary, light blue accent, Arial fonts)
Registering a Custom Theme
import { registerTheme } from 'presentx';
registerTheme({
name: 'my-brand',
colors: {
primary: '#1a1a2e',
accent: '#e94560',
text: '#333333',
textLight: '#888888',
bgDark: '#1a1a2e',
bgLight: '#f5f5f5',
},
fonts: {
heading: 'Montserrat',
body: 'Inter',
},
chartPalette: ['#1a1a2e', '#e94560', '#0f3460', '#16213e', '#533483'],
// Optional: additional named colors for the LLM prompt
extendedColors: {
borders: '#cccccc',
'subtle bg': '#e8e8e8',
},
// Optional: table styling
tableStyle: {
headerBg: '#1a1a2e',
headerText: '#ffffff',
headerTextTransform: 'uppercase',
headerFontWeight: '700',
evenRowBg: '#ffffff',
oddRowBg: '#f5f5f5',
rowText: '#333333',
rowBorder: true,
rowBorderColor: 'rgba(0,0,0,0.08)',
},
});Brand Injection
Add a brand property to your theme to auto-inject logos, headers, and footers on every slide:
registerTheme({
name: 'my-brand',
colors: { /* ... */ },
fonts: { /* ... */ },
chartPalette: [ /* ... */ ],
brand: {
logo: '<svg>...</svg>', // Full wordmark (light bg)
logoDark: '<svg>...</svg>', // Full wordmark (dark bg)
logoIcon: '<svg>...</svg>', // Small icon mark (light bg)
logoIconDark: '<svg>...</svg>', // Small icon mark (dark bg)
name: 'My Company',
header: {
enabled: true, // Show header bar on body slides
content: 'icon', // 'logo' | 'icon' | 'name'
position: 'right', // 'left' | 'right'
height: 36,
},
footer: {
enabled: true, // Show footer bar
showLogo: true,
logoPosition: 'right',
copyright: '© 2025 My Company',
copyrightPosition: 'left',
height: 32,
showBorder: false,
},
},
});Three slide types get different brand treatment:
- Title slide (first): Full wordmark logo at top-left + copyright footer
- Closing slide (last): Same as title slide
- Body slides (middle): Icon in header bar + full footer with logo and copyright
The LLM prompt automatically includes branding notices so the model knows not to manually add logos.
Slide HTML Format
Slides use Tailwind CSS classes inside 960x540px containers:
<div class="slide w-[960px] h-[540px] overflow-hidden relative flex flex-col pt-[48px] pb-[44px] px-[40px]">
<div class="shrink-0 pb-2 mb-3 border-b border-black/[0.06]">
<h2 class="text-[28px] font-bold leading-tight">Slide Title</h2>
</div>
<div class="flex-grow min-h-0 flex flex-col">
<!-- Body content fills remaining space -->
</div>
</div>Charts
Chart.js data embedded as JSON inside the slide:
<div class="slide w-[960px] h-[540px] overflow-hidden relative flex flex-col">
<div class="flex-grow">
<canvas data-chart-type="bar"></canvas>
</div>
<script type="application/json" class="chart-data">
{"labels":["Q1","Q2","Q3","Q4"],"datasets":[{"label":"Revenue","data":[100,200,300,400]}]}
</script>
</div>Charts render as native editable charts in the exported PPTX.
API Reference
Main (presentx)
| Export | Description |
|--------|-------------|
| convertSlidesToPptx(html, title, options?) | Convert HTML slides to PPTX blob |
| validatePresentation(html) | Validate and self-heal slide HTML |
| registerTheme(theme) | Register a custom theme |
| getTheme(name?) | Get a theme by name (default if omitted) |
| listThemes() | List registered theme names |
| injectBrand(html, theme) | Inject brand elements into slide HTML |
| getBrandCss(theme) | Get brand CSS rules for a theme |
| countCompleteSlides(html) | Count completed slides in HTML |
| extractCompleteSlides(html) | Extract only complete slide divs |
| hasSlideContent(html) | Check if HTML contains slides |
React (presentx/react)
| Export | Description |
|--------|-------------|
| SlideViewer | Streaming slide viewer component (props: html, isStreaming, theme, className, onValidationReport) |
| SlideCounter | Slide count indicator component |
Prompts (presentx/prompts)
| Export | Description |
|--------|-------------|
| getSlideInstructions(options?) | Generate LLM system prompt for slide creation |
| getRoutingRules() | Get artifact routing table entry |
| getArtifactTypeDescription() | Get artifact type description string |
| registerTheme(theme) | Register a theme (also available from main entry) |
| getTheme(name?) | Get a theme (also available from main entry) |
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Author
Varun Muppidi -- GitHub
