I'm rendering PDFs in a Next.js@15.2.4
app using pdfjs-dist@4.10.38
. My viewer works perfectly in a plain React app with the same code and versions, but in the Next.js app, the PDF text appears corrupted.
Here's a simplified version of my setup:
PDFViewer.tsx (the main component)
import { GlobalWorkerOptions, getDocument } from "pdfjs-dist";
import { useEffect, useState } from "react";
import PDFPage from "./PDFPage";
GlobalWorkerOptions.workerSrc = "/pdf.worker.min.js";
export default function PDFViewer() {
const [pdfDoc, setPdfDoc] = useState(null);
useEffect(() => {
getDocument("/pdf/example.pdf").promise.then(setPdfDoc);
}, []);
if (!pdfDoc) return null;
return (
<>
{Array.from({ length: pdfDoc.numPages }, (_, i) => (
<PDFPage key={i} pdf={pdfDoc} pageNumber={i + 1} scale={1.5} />
))}
</>
);
}
PDFPage.tsx (the child component)
import { useEffect, useRef, useState } from "react";
export default function PDFPage({ pdf, pageNumber, scale }) {
const canvasRef = useRef(null);
const [viewport, setViewport] = useState(null);
useEffect(() => {
let cancelled = false;
pdf.getPage(pageNumber).then((page) => {
const vp = page.getViewport({ scale });
setViewport(vp);
const canvas = canvasRef.current;
const context = canvas.getContext("2d");
const dpr = window.devicePixelRatio || 1;
canvas.width = vp.width * dpr;
canvas.height = vp.height * dpr;
canvas.style.width = `${vp.width}px`;
canvas.style.height = `${vp.height}px`;
context.scale(dpr, dpr);
const renderTask = page.render({ canvasContext: context, viewport: vp });
renderTask.promise.catch((err) => {
if (!cancelled) console.error("Render error:", err);
});
});
return () => {
cancelled = true;
};
}, [pdf, pageNumber, scale]);
return (
<div>
<canvas ref={canvasRef} />
</div>
);
}
Despite the nearly identical code in both projects, only the Next.js version renders the PDF text incorrectly.
Any idea why the rendering behaves differently in Next.js and how to fix it?
The global attributes in the app layout.tsx
component caused this issue.
lang="he" dir="rtl"
<html lang="he" dir="rtl">
<body className={classNames(styles.body, heebo.className)}>
The PDF was fixed when I removed the attributes:
<html>
<body>
The solution was to update the `canvas`:
<canvas
ref={canvasRef}
style={{ display: "block", direction: "ltr" }}
dir="ltr"
/>