fix(pdf-viewer): load PDF.js from CDN instead of npm

- Loads PDF.js from CDN to avoid SSR issues
- Component handles loading state while PDF.js initializes
- No more dynamic import errors
This commit is contained in:
2026-03-23 21:54:13 +01:00
parent 76e37e2363
commit 746615e095
2 changed files with 37 additions and 30 deletions
+35 -13
View File
@@ -1,10 +1,12 @@
"use client";
import { useState, useRef, useEffect } from "react";
import * as pdfjsLib from "pdfjs-dist";
// Set worker source - use CDN
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
declare global {
interface Window {
pdfjsLib: any;
}
}
export default function PDFViewerClient() {
const [pdfData, setPdfData] = useState<string | null>(null);
@@ -14,11 +16,27 @@ export default function PDFViewerClient() {
const [numPages, setNumPages] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
const [scale, setScale] = useState(1.5);
const [pdfjsLoaded, setPdfjsLoaded] = useState(false);
const canvasRef = useRef<HTMLCanvasElement>(null);
const pdfDocRef = useRef<any>(null);
useEffect(() => {
// Load PDF.js from CDN
const script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js";
script.async = true;
script.onload = () => {
window.pdfjsLib = window.pdfjsLib;
setPdfjsLoaded(true);
};
script.onerror = () => {
setError("Failed to load PDF.js library");
};
document.body.appendChild(script);
}, []);
const renderPage = async (pageNum: number) => {
if (!pdfDocRef.current || !canvasRef.current) return;
if (!pdfDocRef.current || !canvasRef.current || !window.pdfjsLib) return;
try {
const page = await pdfDocRef.current.getPage(pageNum);
@@ -39,14 +57,14 @@ export default function PDFViewerClient() {
};
useEffect(() => {
if (pdfDocRef.current && currentPage) {
if (pdfDocRef.current && currentPage && pdfjsLoaded) {
renderPage(currentPage);
}
}, [currentPage, scale]);
}, [currentPage, scale, pdfjsLoaded]);
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
if (!file || !window.pdfjsLib) return;
if (file.type !== "application/pdf") {
setError("Please select a PDF file");
@@ -60,7 +78,7 @@ export default function PDFViewerClient() {
try {
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
pdfDocRef.current = pdf;
setNumPages(pdf.numPages);
setPdfData("local");
@@ -74,7 +92,7 @@ export default function PDFViewerClient() {
const handleUrlSubmit = async () => {
const input = prompt("Enter PDF URL:");
if (!input) return;
if (!input || !window.pdfjsLib) return;
setLoading(true);
setError(null);
@@ -86,7 +104,7 @@ export default function PDFViewerClient() {
const response = await fetch(input);
const arrayBuffer = await response.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
pdfDocRef.current = pdf;
setNumPages(pdf.numPages);
setPdfData("url");
@@ -125,21 +143,26 @@ export default function PDFViewerClient() {
accept="application/pdf"
onChange={handleFileUpload}
className="hidden"
disabled={!pdfjsLoaded}
/>
</label>
<button
onClick={handleUrlSubmit}
className="bg-slate-700 hover:bg-slate-600 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
disabled={!pdfjsLoaded}
className="bg-slate-700 hover:bg-slate-600 disabled:opacity-50 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"
>
🔗 Load from URL
</button>
{!pdfjsLoaded && (
<span className="text-slate-400 text-sm">Loading PDF.js...</span>
)}
{pdfData && (
<>
<div className="h-6 w-px bg-slate-600 mx-2" />
{/* Page navigation */}
<button
onClick={goToPrevPage}
disabled={currentPage <= 1}
@@ -160,7 +183,6 @@ export default function PDFViewerClient() {
<div className="h-6 w-px bg-slate-600 mx-2" />
{/* Zoom controls */}
<button
onClick={zoomOut}
className="bg-slate-700 hover:bg-slate-600 text-white px-3 py-2 rounded-lg text-sm font-medium transition-colors"