SlideCanvas is a high-performance, browser-native toolkit for viewing and editing PowerPoint (.pptx) files directly in the web browser. It provides an enterprise-grade engine for parsing, rendering, and exporting presentations with pixel-perfect accuracy
npm install slidecanvasSlideCanvas is a high-performance, browser-native toolkit for viewing and editing PowerPoint (.pptx) files directly in the web browser. It provides an enterprise-grade engine for parsing, rendering, and exporting presentations with pixel-perfect accuracy and seamless S3 integration.
---
- Pixel-Perfect Rendering: High-fidelity display of slides using a sophisticated Fabric.js-based canvas.
- AI-Powered Editing: Integrated Gemini AI for smart text manipulation (Shorten, Reframe, Lengthen, Grammar, Rewrite) with side-by-side controls and custom "Ask AI" input.
- Enterprise-Grade Parsing: Deep internal processing of XML-based PPTX structures.
- Professional Ribbon UI: A high-density, Microsoft Office-style toolbar layout (120px height) with optimized visibility for all controls.
- Slash Commands (/): A fast, context-aware command menu triggered by typing / anywhere on the canvas or in a text box.
- AI-Powered Image Generation: Integrated flow for generating design assets via optional onGenerateImage hook, with built-in preview and replacement logic.
- Two-Step Infographic Generation: Advanced workflow to refine selected slide text into visual prompts before generating infographics (via onRefineInfographicPrompt and onGenerateInfographic).
- AI Suggestion Box: A side-by-side text refinement UI for reviewing Shorten/Reframe/Lengthen suggestions before applying them.
- Smart Actions Group: Prominent buttons for Present, Export (PPTX), and Delete, with a subtle "Saved" status indicator.
- Professional Export: High-quality .pptx generation with support for transparency and layout mapping.
- S3 & Remote Support: Built-in architecture for loading presentations via secure proxy tunnels.
---
Install SlideCanvas via your preferred package manager:
``bash`
npm install slidecanvasor
yarn add slidecanvasor
pnpm add slidecanvas
---
SlideCanvas operates on a sophisticated Unidirectional Data Flow architecture designed for reliability and scalability:
1. Ingestion Layer: A multi-stage processing engine that decomposes binary .pptx uploads or remote URLs.
2. Normalization Engine: Converts complex XML structures into a standardized, lightweight JSON presentation state.
3. Virtual Canvas Sync: Bridges the presentation state to a high-performance Fabric.js canvas, handling real-time manipulation and absolute positioning.
4. Serialization Pipe: Reconstructs the internal state back into standard OpenXML structures for high-fidelity export.
> [!NOTE]
> All parsing and state transformations occur within a secure, sandboxed internal context to ensure document integrity.
---
To build a basic editor, import the PptEditor component into your React/Next.js application:
`tsx
import { PptEditor } from 'slidecanvas';
export default function MyEditor() {
return (
);
}
`
SlideCanvas makes it easy to load existing presentations directly via a URL. The component handles the fetching and parsing internally:
`tsx
import { PptEditor } from 'slidecanvas';
export default function App() {
const pptxUrl = "https://example.com/presentations/q1-report.pptx";
// Custom proxy endpoint (defaults to undefined, causing direct fetch)
// If your app has an API route at /api/proxy, specify it here:
return
}
`
---
SlideCanvas supports loading files from S3 or public URLs. To bypass CORS restrictions, we recommend setting up a proxy route in your api/proxy/route.ts:
`typescript
// Next.js API Proxy Example
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const targetUrl = searchParams.get('url');
const response = await fetch(targetUrl!, { cache: 'no-store' });
return new NextResponse(response.body, {
status: 200,
headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.presentationml.presentation' }
});
}
`
> [!IMPORTANT]
> To use the custom proxy above, pass its path to the proxyUrl prop in the PptEditor component (e.g., ).
prop. This is ideal for downloading files directly from S3 using pre-signed URLs.`tsx
fetchPresentation={async (url) => {
// Logic to fetch directly from client
// Example: Fetch pre-signed URL then download file
const response = await fetch(url);
if (!response.ok) throw new Error("Fetch failed");
return await response.blob();
}}
/>
`$3
SlideCanvas features a powerful Slash Command system. Type / at any time to:
- Insert Elements: Instantly add Text, Images, or Shapes.
- AI Actions: Trigger text transformations or AI Image Generation directly at your cursor.---
$3
You can integrate your own AI image provider (e.g., DALL-E, Midjourney, or a custom S3-backed service) by passing the onGenerateImage prop:`tsx
onGenerateImage={async (prompt) => {
const response = await fetch('/api/my-ai', { body: JSON.stringify({ prompt }) });
const data = await response.json();
return data.s3ImageUrl; // Return a URL string
}}
/>
`> [!TIP]
> When an image is generated, SlideCanvas provides a professional Preview Modal allowing users to Insert as new, Replace a selected image (preserving dimensions), or Discard.
$3
SlideCanvas now supports a full suite of custom AI handlers, allowing you to bypass the internal Gemini implementation and use your own backend for every AI action.
`tsx
// ... other props
// Toolbar AI Actions (Quick Actions)
onRefineShorten={async (text) => myApi.shorten(text)}
onRefineReframe={async (text) => myApi.reframe(text)}
onRefineLengthen={async (text) => myApi.lengthen(text)} // Slash Menu "Ask AI" Actions
onAiEdit={async (text, prompt) => myApi.customEdit(text, prompt)} // Handles the custom input field
onAiRewrite={async (text) => myApi.rewrite(text)}
onAiGrammar={async (text) => myApi.fixGrammar(text)}
onAiShorten={async (text) => myApi.shorten(text)}
onAiLengthen={async (text) => myApi.lengthen(text)}
onAiContinue={async (text) => myApi.continueWriting(text)}
/>
`$3
(Two-Step Flow)
SlideCanvas supports a sophisticated two-step workflow for creating infographics from slide content. This is enabled via the isTwoStepInfographicGeneration prop.1. Step 1: Refinement: The selected text is sent to
onRefineInfographicPrompt. The user previews and edits the AI-generated visual prompt.
2. Step 2: Generation: The refined prompt is then sent to onGenerateInfographic to create the final asset.`tsx
isTwoStepInfographicGeneration={true}
onRefineInfographicPrompt={async (text) => {
// Use LLM to turn slide text into a visualization prompt
return await myAiRefine(text);
}}
onGenerateInfographic={async (prompt) => {
// Generate the actual chart/infographic image
return await myImageGen(prompt);
}}
/>
`$3
SlideCanvas comes battery-included with Gemini AI support for text.`tsx
import { PptEditor } from 'slidecanvas';export default function MyEditor() {
const geminiKey = process.env.NEXT_PUBLIC_GEMINI_API_KEY;
return (
geminiApiKey={geminiKey}
appName="AI Design Studio"
/>
);
}
`$3
While SlideCanvas includes built-in Gemini support, you can override the logic with your own AI providers (OpenAI, Anthropic, or custom internal LLMs) using the following hooks:`tsx
onAiRewrite={async (text) => {
const res = await myAiService.call('rewrite', text);
return res.output;
}}
onAiGrammar={async (text) => {
return await myAiService.call('fix-grammar', text);
}}
onAiShorten={async (text) => {
return await myAiService.call('summarize', text);
}}
// Also supports onAiLengthen and onAiContinue
/>
`> [!NOTE]
> When these hooks are provided, the editor will favor them over the default Gemini integration.
$3
The editor separates "Quick Actions" (Toolbar) from "Conversational AI" (Slash Command). You can provide distinct handlers for each:- Toolbar (Refine Menu): Uses
onRefineShorten, onRefineReframe, onRefineLengthen.
- Slash Command (Ask AI): Uses onAiShorten, onAiLengthen, onAiRewrite, etc.This allows you to use lighter/faster models for the toolbar buttons and more capable models for the conversational interface if desired.
Advanced Usage Examples
$3
To save changes back to S3 after a modifications stop for 7 seconds, use the
PptxBlobExporter combined with a debounce pattern. Unlike the regular exporter, this utility returns a Blob directly without triggering a browser download.`tsx
import { PptEditor, PptxBlobExporter } from 'slidecanvas';
import { useRef } from 'react';export function S3Editor({ s3Key }: { s3Key: string }) {
const timerRef = useRef(null);
const handleAutoSave = (presentation: any) => {
// 1. Clear previous timer if user keeps editing
if (timerRef.current) clearTimeout(timerRef.current);
// 2. Start a new 7-second countdown
timerRef.current = setTimeout(async () => {
console.log('User stopped editing. Generating Blob and saving to S3...');
// 3. Generate the .pptx file binary as a Blob
const exporter = new PptxBlobExporter();
const blob = await exporter.exportToBlob(presentation);
// 4. Upload to your backend API which talks to S3
const formData = new FormData();
formData.append('file', blob, 'update.pptx');
formData.append('key', s3Key);
await fetch('/api/save-to-s3', {
method: 'POST',
body: formData
});
console.log('Saved successfully!');
}, 7000); // 7 seconds
};
return ;
}
`#### Sample Backend (Next.js API Route)
`typescript
// api/save-to-s3/route.ts
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";export async function POST(req: Request) {
const data = await req.formData();
const file = data.get('file') as File;
const key = data.get('key') as string;
const buffer = Buffer.from(await file.arrayBuffer());
const s3 = new S3Client({ region: "ap-south-1" });
await s3.send(new PutObjectCommand({
Bucket: "your-bucket-name",
Key: key,
Body: buffer,
ContentType: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
}));
return Response.json({ success: true });
}
`$3
You can skip the parser and build a presentation state manually to generate slides on the fly:`tsx
import { PptEditor, Presentation } from 'slidecanvas';const myDraft: Presentation = {
slides: [
{
id: 'welcome-slide',
elements: [
{
id: 'title-1',
type: 'text',
content: 'Hello World from SlideCanvas!',
x: 100, y: 100, width: 600, height: 100, fontSize: 48,
color: '#3b82f6', zIndex: 1
}
]
}
],
layout: { width: 12192000, height: 6858000 } // Standard 16:9 EMU
};
export default function App() {
return ;
}
`$3
Extract text content or images without rendering the UI:`typescript
import { PptxParser } from 'slidecanvas';async function extractCaptions(fileBuffer: ArrayBuffer) {
const parser = new PptxParser();
const presentation = await parser.parse(fileBuffer);
const allText = presentation.slides.flatMap(slide =>
slide.elements
.filter(el => el.type === 'text')
.map(el => el.content)
);
return allText;
}
`---
API Reference
$3
$3
| Property | Type | Default | Description |
| :--- | :--- | :--- | :--- |
|
width | number \| string | Required | The width of the editor container. |
| height | number \| string | Required | The height of the editor container. |
| initialPresentation | Presentation | undefined | Initial presentation data to load. |
| url | string | undefined | URL of a public .pptx file to load on mount. |
| initialSource | 'scratch' \| 'uploaded' \| 'url' | undefined | Sets the initial UI source state and badge. |
| appName | string | "SlideCanvas" | Brand name shown in the ribbon. |
| appBgColor | string | "#B7472A" | Primary brand color for the UI. |
| geminiApiKey | string | undefined | API key for built-in AI text actions. |
| onGenerateImage | (prompt: string) => Promise | undefined | Custom hook to handle AI image generation. |
| isTwoStepInfographicGeneration | boolean | false | Enables Step 1: Prompt Refinement modal for infographics. |
| onRefineInfographicPrompt | (text: string) => Promise | undefined | Hook to transform text into a visual prompt (Step 1). |
| onGenerateInfographic | (prompt: string) => Promise | undefined | Hook to generate the infographic image (Step 2). |
| proxyUrl | string | undefined | Base path for the proxy API (used for PPTX loading and image previews). |
| showHomeOnEmpty | boolean | false | Shows a "New / Upload" splash if no data provided. |
| onChange | (pres: Presentation) => void | undefined | Fired on any change to the deck. |
| onSourceChange | (src, url?) => void | undefined | Fired when the deck origin changes (useful for routing). |
| onAiRewrite | (text: string) => Promise | undefined | Optional hook to override default Gemini rewrite logic. |
| onAiGrammar | (text: string) => Promise | undefined | Optional hook to override default Gemini grammar logic. |
| onAiShorten | (text: string) => Promise | undefined | Optional hook to override default Gemini shorten logic. |
| onAiLengthen | (text: string) => Promise | undefined | Optional hook to override default Gemini lengthen logic. |
| onAiContinue | (text: string) => Promise | undefined | Optional hook to override default Gemini continue-writing logic. |
| onRefineShorten | (text: string) => Promise | undefined | Override loop for Toolbar "Shorten" button. |
| onRefineReframe | (text: string) => Promise | undefined | Override loop for Toolbar "Reframe" button. |
| onRefineLengthen | (text: string) => Promise | undefined | Override loop for Toolbar "Lengthen" button. |
| fetchPresentation | (url: string) => Promise | undefined | Custom fetcher for loading presentations, bypassing the proxy. |$3
#### 1. Branded Full-Screen Editor
Perfect for a standalone presentation app.
`tsx
import { PptEditor } from 'slidecanvas';export default function MyEditor() {
return (
width="100%"
height="100%"
appName="SkyDeck"
appBgColor="#0f172a" // Custom dark theme
showHomeOnEmpty={true} // Show the splash screen if no PPT loaded
/>
);
}
`#### 2. AI-Powered Assistant
Enable Gemini-driven text refinements and slide generation.
`tsx
width={1200}
height={800}
geminiApiKey={process.env.NEXT_PUBLIC_GEMINI_API_KEY}
appName="AI Slides"
/>
`#### 3. Deep-Linking & URL Sync (Next.js)
Synchronize the editor's source (Scratch, Uploaded, or Remote) with your browser's address bar.
`tsx
const router = useRouter();
const searchParams = useSearchParams(); width="100vw"
height="100vh"
url={searchParams.get('url')}
initialSource={searchParams.get('source')} // 'scratch' | 'uploaded' | 'url'
onSourceChange={(source, url) => {
// Dynamically update URL as user switches between 'Create New' and 'Upload'
const query = url ?
?url=${url} : ?source=${source};
router.push(/editor${query});
}}
/>
`$3
#### Visual Shape Selection
The editor includes a rich selection of 30+ SVG shapes out of the box. The shapes are fully responsive and scale accurately with the editor's dimensions.
#### Professional Ribbon UI
SlideCanvas features a sophisticated 120px high ribbon layout divided into logical groups:
- Slides: New slide creation and layout switching (Title, Content, Split).
- Font: Full-width family and size selectors with comprehensive formatting tools.
- Drawing: Instant access to Shapes, Text Boxes, and Image uploads.
- AI Text: Side-by-side buttons for intelligent content transformation.
- Actions: Large, accessible buttons for primary workflows like Presenting and Exporting.
#### Intelligent Routing
Use the
onSourceChange callback to synchronize the editor state with your application's routing. This allows for deep-linking to specific presentations or maintaining "Upload" vs "New" states in the address bar.$3
For headless workflows, you can use the internal engine directly:
`typescript
import { PptxParser, PptxExporter, PptxBlobExporter } from 'slidecanvas';// 1. Load: Parse a .pptx file into JSON
const parser = new PptxParser();
const presentation = await parser.parse(myArrayBuffer);
// 2. Export: Trigger a browser file download
const exporter = new PptxExporter();
await exporter.export(presentation);
// 3. Blob Export: Get file data as a Blob (useful for S3/API uploads)
const blobExporter = new PptxBlobExporter();
const blob = await blobExporter.exportToBlob(presentation);
`---
Building for Production
To build your application for production, run:
`bash
npm run build
`The
slidecanvas` library is optimized for tree-shaking and minimizes your final bundle size by lazily loading the Fabric.js engine.---
This project is licensed under the MIT License. You are free to use, modify, and distribute this software as permitted by the license terms.