react-pdf-kit-nextjs-app-router
Use this skill when: the developer asks to add a PDF viewer using
@react-pdf-kit/viewer to a Next.js App Router project (app/
directory) on Next.js 15+ (with Turbopack). For Pages Router use
react-pdf-kit-nextjs-pages-router. Next.js 14 is not supported by
v2 — see react-pdf-kit-nextjs14-pdfjs-override.
The viewer is client-only: it relies on browser APIs and cannot run on the server. The skill structures the integration around that constraint.
Gotchas
- The viewer cannot render on the server. Importing it into a
Server Component (or letting Next SSR it) throws
TypeError: Promise.withResolve is not a function. Every component that imports from@react-pdf-kit/viewerneeds'use client', AND must be mounted through anext/dynamicimport withssr: false. - Install only
@react-pdf-kit/viewer. Do NOT installpdfjs-distseparately. It is an auto-installed peer dependency. No worker configuration is required either —RPConfighandles it. Custom worker URLs / version overrides live inreact-pdf-kit-worker-config. RPConfigshould be rendered once, at the root layout, so the license and config apply app-wide. The page-level viewer mounts theRPProvider/RPLayout/RPPageschain.- Use Turbopack on Next.js 15 (
next dev --turbopack) for the smoothest experience. Next.js 14 + webpack is unsupported by v2. - Hydration: render the viewer inside a fixed-height container so layout doesn't shift. Without a definite height the virtualizer can mount with 0 rows.
RPDefaultLayoutis deprecated in v2. UseRPLayout.
Procedure
1. Install the library
pnpm add @react-pdf-kit/viewer
2. Put RPConfig at the root layout (client-only)
// app/components/AppProviders.tsx
'use client'
import { RPConfig, type RPConfigProps } from '@react-pdf-kit/viewer'
import { type PropsWithChildren } from 'react'
export default function AppProviders({
children,
...props
}: PropsWithChildren<RPConfigProps>) {
return <RPConfig {...props}>{children}</RPConfig>
}
// app/components/LazyAppProviders.tsx
'use client'
import dynamic from 'next/dynamic'
const LazyAppProviders = dynamic(() => import('./AppProviders'), {
ssr: false,
})
export default LazyAppProviders
// app/layout.tsx
import { type PropsWithChildren } from 'react'
import LazyAppProviders from './components/LazyAppProviders'
export default function RootLayout({ children }: PropsWithChildren) {
return (
<html lang="en">
<body>
<LazyAppProviders licenseKey="your-license-key">
<main>{children}</main>
</LazyAppProviders>
</body>
</html>
)
}
licenseKey is optional; without it the viewer runs in trial mode
(watermark).
3. Create the viewer and its lazy wrapper
// app/components/AppPdfViewer.tsx
'use client'
import { RPProvider, RPLayout, RPPages } from '@react-pdf-kit/viewer'
export default function AppPdfViewer({ pdfSrc }: { pdfSrc: string }) {
return (
<RPProvider src={pdfSrc}>
<RPLayout toolbar>
<RPPages />
</RPLayout>
</RPProvider>
)
}
// app/components/LazyAppPdfViewer.tsx
'use client'
import dynamic from 'next/dynamic'
const LazyAppPdfViewer = dynamic(() => import('./AppPdfViewer'), {
ssr: false,
})
export default LazyAppPdfViewer
4. Use it in a page inside a definite-height container
// app/document/[id]/page.tsx
import LazyAppPdfViewer from '../../components/LazyAppPdfViewer'
export default function DocumentPage({ params }: { params: { id: string } }) {
return (
<main style={{ height: '100vh' }}>
<LazyAppPdfViewer pdfSrc={`/api/documents/${params.id}.pdf`} />
</main>
)
}
Verify
pnpm install
pnpm build
pnpm dev --turbopack # then visit /document/<id>
Open the page. The first PDF page should render with the default
toolbar. Scroll to confirm virtualization, and select text on a
rendered page to confirm the text layer mounted. There should be no
Promise.withResolve is not a function error in the console.
References
- Next.js dynamic import: https://nextjs.org/docs/app/api-reference/functions/dynamic
- Companion skills:
react-pdf-kit-nextjs14-pdfjs-override: read this if the project is on Next.js 14 (v2 is unsupported there).react-pdf-kit-worker-config: for overridingpdfjs-distand the Next.js 15 / TurbopackworkerUrlrecipe.