How to Convert Word Documents and Slides to Images with Next.js
Convert DOCX, PPTX, and other Office files to page images in a Next.js app with a PDF-based workflow, a slide preview option, and deployment guidance.
For reliable Word and PowerPoint page images in a Next.js application, use a server-side pipeline: accept and validate the DOC, DOCX, PPT, or PPTX file; convert it to PDF; then rasterize each PDF page to PNG, JPEG, or WebP with a separate PDF renderer. Next.js provides the application framework and request handling, not a built-in Office-to-image converter. For PPTX-only previews, a JavaScript slide renderer can avoid the PDF step, provided its rendering fidelity meets your needs.
This guide shows the architecture, a runnable Next.js upload endpoint using LibreOffice and Poppler command-line tools, a managed Office-to-PDF alternative, and a direct PPTX preview option. The code is a reference implementation: install and validate the chosen converter and fonts in the same runtime environment where it will run.
1. Choose a conversion path
| Approach | Good fit | Tradeoffs |
|---|---|---|
| Office file → PDF → page images | DOC/DOCX and PPT/PPTX support through one workflow; consistent page-oriented output. | Requires an Office converter and a separate PDF rasterizer. Validate fonts and layout fidelity. |
| JavaScript PPTX preview | Slide previews when SVG or PNG output is sufficient. | PPTX-focused and documented as a high-fidelity preview, not a spec-complete paint engine. Package is pre-1.0; pin the version. |
| Managed Office-to-PDF API | When operating conversion binaries is undesirable and sending files to a service is acceptable. | Check data handling and service costs. The cited Adobe API documents Office-to-PDF; PDF-to-image remains a separate stage. |
Adobe PDF Services documents DOC, DOCX, PPT, and PPTX as inputs for creating PDF files, and lists Node.js SDK support. This supports the first stage only; select a PDF rasterizer for the image output. See Adobe PDF Services API documentation.
For direct slide previews, Office Kit documents SVG rendering and a companion preview package with SVG/PNG output options. Its documentation describes the renderer as a high-fidelity preview and points to PowerPoint or LibreOffice headless for pixel-authoritative output. Since the project is pre-1.0 and API changes may be breaking, pin the exact package version and validate representative decks.
2. The server-side pipeline
- Receive and validate. Restrict accepted extensions and MIME types, set an upload size limit, and use generated temporary filenames. Do not trust the client-provided filename or MIME type by itself.
- Convert Office to PDF. Run LibreOffice headlessly in an environment that has the executable and required fonts, or call a managed Office-to-PDF API.
- Rasterize pages. Use a PDF renderer such as Poppler’s
pdftoppmto write one image per page. Choose format and resolution for your preview or export use case. - Return or store results. Return images, a ZIP/archive, or durable object-storage URLs according to the product flow. Remove temporary inputs and intermediate PDFs, including on errors.
For the Pages Router, Next.js documents that getServerSideProps runs on every request and generates page HTML per request. For uploads and conversions, use an API route or route handler appropriate to your Next.js version and deployment architecture; conversion belongs in server-side application code or a separate service.
3. Runnable Next.js example with LibreOffice and Poppler
This Pages Router API route accepts one Office file, converts it to PDF with LibreOffice, rasterizes the first page to PNG with Poppler, and returns that PNG. It deliberately limits input size and output page count for a simple request/response example. For multi-page documents, adapt the final stage to return a ZIP or store pages and return their URLs.
Install the Node dependency and system tools in the deployment image:
npm install formidable
# Install LibreOffice and Poppler using your operating system or container package manager.
# Confirm that `soffice` and `pdftoppm` are on PATH.
Create pages/api/office-preview.js:
import formidable from 'formidable';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { randomUUID } from 'node:crypto';
import { promisify } from 'node:util';
import { execFile } from 'node:child_process';
const execFileAsync = promisify(execFile);
const MAX_BYTES = 20 * 1024 * 1024;
const ALLOWED = new Set(['.doc', '.docx', '.ppt', '.pptx']);
export const config = {
api: { bodyParser: false },
};
function first(value) {
return Array.isArray(value) ? value[0] : value;
}
export default async function handler(req, res) {
if (req.method !== 'POST') {
res.setHeader('Allow', 'POST');
return res.status(405).json({ error: 'Use POST with a multipart file field named file.' });
}
const workDir = await fs.mkdtemp(path.join(os.tmpdir(), 'office-preview-'));
try {
const form = formidable({
multiples: false,
maxFileSize: MAX_BYTES,
maxFiles: 1,
uploadDir: workDir,
keepExtensions: true,
});
const [fields, files] = await form.parse(req);
const uploaded = first(files.file);
if (!uploaded) return res.status(400).json({ error: 'Missing multipart field: file.' });
const ext = path.extname(uploaded.originalFilename || '').toLowerCase();
if (!ALLOWED.has(ext)) return res.status(415).json({ error: 'Supported extensions: .doc, .docx, .ppt, .pptx.' });
if (uploaded.size > MAX_BYTES) return res.status(413).json({ error: 'File exceeds the 20 MiB limit.' });
const inputPath = uploaded.filepath;
const pdfPath = path.join(workDir, 'converted.pdf');
const profilePath = path.join(workDir, 'lo-profile');
const outputPrefix = path.join(workDir, 'page');
await execFileAsync('soffice', [
`-env:UserInstallation=file://${profilePath}`,
'--headless', '--convert-to', 'pdf', '--outdir', workDir, inputPath,
], { timeout: 120_000, maxBuffer: 2 * 1024 * 1024 });
// LibreOffice names the PDF after the uploaded temporary file.
const generatedPdf = path.join(workDir, `${path.basename(inputPath, path.extname(inputPath))}.pdf`);
await fs.rename(generatedPdf, pdfPath);
// -f 1 -l 1 renders just page one; -r controls DPI; -png selects PNG output.
await execFileAsync('pdftoppm', ['-f', '1', '-l', '1', '-r', '144', '-png', pdfPath, outputPrefix], {
timeout: 60_000,
maxBuffer: 2 * 1024 * 1024,
});
const pngPath = `${outputPrefix}-1.png`;
const image = await fs.readFile(pngPath);
res.setHeader('Content-Type', 'image/png');
res.setHeader('Content-Disposition', 'inline; filename="page-1.png"');
res.setHeader('Cache-Control', 'no-store');
return res.status(200).send(image);
} catch (error) {
const tooLarge = error?.httpCode === 413 || error?.code === 1009;
const timedOut = error?.killed || error?.signal === 'SIGTERM';
console.error('Office preview conversion failed', {
code: error?.code,
message: error?.message,
stderr: error?.stderr,
});
return res.status(tooLarge ? 413 : timedOut ? 504 : 500).json({
error: tooLarge ? 'File is too large.' : timedOut ? 'Conversion timed out.' : 'Could not convert this document.',
});
} finally {
await fs.rm(workDir, { recursive: true, force: true }).catch(() => {});
}
}
Send a multipart request from a browser or client. A minimal browser example:
const form = new FormData();
form.append('file', fileInput.files[0]);
const response = await fetch('/api/office-preview', { method: 'POST', body: form });
if (!response.ok) throw new Error(await response.text());
const imageUrl = URL.createObjectURL(await response.blob());
previewImage.src = imageUrl;
Do not manually set the multipart Content-Type header in the browser; fetch supplies the boundary. For a production multi-page workflow, inspect the PDF page count, apply a page limit, rasterize pages in bounded batches, and package or persist results instead of placing a large set of images in one response.
4. A direct JavaScript route for PPTX previews
If you only need PPTX previews and accept preview-renderer fidelity, Office Kit documents slide rendering to SVG and PNG via its companion preview package. Package names and exports can change while the project is pre-1.0, so follow its current README, install and pin an exact version, and keep a fixture deck set for validation. This conceptual server-side outline intentionally avoids inventing package API calls that may change:
// Install the exact version documented by the Office Kit project and pin it in your lockfile.
// Load the PPTX, render each slide with the companion preview package,
// then serialize SVG or encode the provided PNG output for storage/response.
// See the project's current API and examples:
// https://github.com/office-kit/office-kit
For output that must match PowerPoint closely, use PowerPoint or LibreOffice headless and compare actual exported slides. A direct preview library is a different fidelity choice, not a guaranteed substitute for an Office rendering engine.
5. Options that affect output
| Choice | Effect | Practical guidance |
|---|---|---|
| Output format | PNG preserves sharp edges and supports transparency; JPEG is often smaller for photographic pages; WebP can reduce size where consumers support it. | Choose based on downstream compatibility and whether transparency matters. |
| Resolution / DPI | Higher resolution creates larger images and uses more CPU and memory. | For small previews start with a moderate DPI, then inspect text legibility at the display size. Fix the chosen DPI in your API contract. |
| Page selection | Rendering every page increases work and output size. | Support first-page preview, selected pages, or an explicit maximum; reject or queue unusually long files. |
| Fonts | Missing fonts can change line wrapping, spacing, and pagination. | Install the fonts your input documents require where legally permitted, and validate fallback behavior. |
| Temporary storage | Conversion produces the source upload, intermediate PDF, and page images. | Use a per-request temporary directory, clean it in a finally block, and monitor disk availability. |
| Conversion location | Local binaries give control over the runtime; a managed API moves conversion operations to a service. | Review file privacy, retention, network transfer, service terms, and cost before selecting a hosted converter. |
6. Deployment, performance, reliability, and cost
Next.js identifies a Node.js server as a baseline runtime and documents deployment on Node.js servers or Docker containers. A converter adds its own requirements: executable binaries or a compatible WebAssembly runtime, fonts, writable temporary storage, memory, and enough request duration. Do not assume an Edge runtime or every serverless host can run LibreOffice. Check your host’s current limits and build a deployment image that includes the converter dependencies.
Conversion time and memory depend on the document, page count, embedded content, selected DPI, fonts, and runtime. The research sources do not provide an independent performance benchmark, so measure with representative files from your users. Bound upload size, page count, DPI, parallel jobs, and execution time. For work that exceeds a request’s duration or memory limits, move conversion to a background worker or separate service and return a job identifier.
Reliability improves when conversion is isolated from the web request process, temporary files are scoped per job, and failures are reported by stage (upload, Office-to-PDF, PDF rasterization, storage). Use unique LibreOffice profiles per concurrent job to avoid profile conflicts. Keep the original input only as long as required, clean intermediates after both success and failure, and make retries safe by assigning a job ID and avoiding duplicate result records.
Self-hosted software does not remove infrastructure cost: account for CPU, memory, disk, container image size, and maintenance. A managed API adds service charges and data-transfer considerations; the cited material does not establish a price for your expected volume. Benchmark and cost-model against your own document mix before committing.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
soffice: command not found |
LibreOffice is missing or not on the process PATH. | Install it in the runtime image and verify soffice --version during deployment checks. |
| LibreOffice exits successfully but no PDF appears | Unexpected temporary filename, unsupported/corrupt input, or conversion diagnostic was missed. | Log stderr and inspect the generated output directory; use a controlled input name and check that the PDF exists before rasterizing. |
| Concurrent requests fail or hang | Shared LibreOffice user profile or resource contention. | Give each job a unique profile directory, cap concurrency, and set process timeouts. |
pdftoppm: command not found |
Poppler utilities are absent from the deployed image. | Install Poppler and confirm the binary is available at runtime. |
| Text or layout differs from the original | Font substitution, renderer differences, unsupported Office features, or a damaged source file. | Install needed fonts, try LibreOffice/PowerPoint output for the fidelity target, and inspect representative complex documents. |
| Only the first page is returned | The example intentionally uses -f 1 -l 1. |
Rasterize the selected page range and return an archive or stored URLs for multiple page images. |
| Request times out or runs out of memory | Large file, many pages, high DPI, or host execution limits. | Reduce limits and resolution, queue the job, or move it to a worker/container with appropriate resources. |
| Upload parser rejects the request | Wrong field name, malformed multipart body, or request exceeds configured limits. | Send a multipart field named file, let the browser set its boundary, and align proxy and application size limits. |
8. Or skip the browser setup
If your goal is a screenshot of a web page or rendered preview URL, rather than conversion of the original Office file, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. It does not convert a DOCX or PPTX file directly; first render or publish the document as a web page that can be captured.
Read the ScreenshotNeo API documentation. cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be disabled. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan.
Sign up for 1,000 free screenshots a month, with no card required.
9. Frequently asked questions
Can Next.js convert a Word file to a PNG by itself?
No. Use a converter in server-side application code or a separate service, then rasterize the resulting PDF.
Can this run on the Edge Runtime?
Do not assume so. LibreOffice requires a compatible executable environment; check your host and runtime support. A remote conversion service is another option.
Can the example return all pages?
Yes. Inspect the PDF page count, rasterize a bounded page range, and return a ZIP or store each image and return references.
Is a browser preview identical to PowerPoint?
Not necessarily. The Office Kit documentation describes preview rendering and recommends PowerPoint or LibreOffice headless when pixel-authoritative output is required.
How should I choose between a library and a hosted converter?
Compare format coverage, output fidelity on representative files, runtime compatibility, file-handling requirements, operational effort, and total cost at your expected volume.


