react-pdf: Unable to render in Next.js 13 app directory route handler

Describe the bug

Using renderToBuffer in a Next.js 13 app directory route handler throws: TypeError: ba.Component is not a constructor. It works as expected when using a route handler inside of the pages directory instead.

full output
- warn ./node_modules/node-fetch/lib/index.js
Module not found: Can't resolve 'encoding' in '/Users/foo/react-pdf-nextjs/node_modules/node-fetch/lib'

Import trace for requested module:
./node_modules/node-fetch/lib/index.js
./node_modules/cross-fetch/dist/node-ponyfill.js
./node_modules/@react-pdf/font/lib/index.cjs.js
./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js
./src/app/api/app.pdf/route.tsx
- error TypeError: ba.Component is not a constructor
    at $$$reconciler (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:631:50)
    at createRenderer (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:3889:12)
    at pdf (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:3997:28)
    at _callee$ (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:4189:32)
    at tryCatch (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:36:25)
    at Generator.eval (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:120:30)
    at Generator.eval [as next] (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:64:29)
    at asyncGeneratorStep (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:4:28)
    at _next (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:17)
    at eval (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:27:13)
    at new Promise (<anonymous>)
    at eval (webpack-internal:///(sc_server)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:19:16)
    at renderToStream (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:4202:21)
    at renderToBuffer (webpack-internal:///(sc_server)/./node_modules/@react-pdf/renderer/lib/react-pdf.cjs.js:4235:12)
    at GET (webpack-internal:///(sc_server)/./src/app/api/app.pdf/route.tsx:14:93)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:265:43)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/lib/trace/tracer.js:111:36)
    at NoopContextManager.with (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:360:30)
    at ContextAPI.with (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:30:58)
    at NoopTracer.startActiveSpan (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:953:34)
    at ProxyTracer.startActiveSpan (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:993:36)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/lib/trace/tracer.js:100:107)
    at NoopContextManager.with (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:360:30)
    at ContextAPI.with (webpack-internal:///(sc_server)/./node_modules/next/dist/compiled/@opentelemetry/api/index.js:30:58)
    at NextTracerImpl.trace (webpack-internal:///(sc_server)/./node_modules/next/dist/server/lib/trace/tracer.js:100:32)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:253:53)
    at AsyncLocalStorage.run (node:async_hooks:330:14)
    at Object.wrap (webpack-internal:///(sc_server)/./node_modules/next/dist/server/async-storage/static-generation-async-storage-wrapper.js:41:24)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:207:97)
    at AsyncLocalStorage.run (node:async_hooks:330:14)
    at Object.wrap (webpack-internal:///(sc_server)/./node_modules/next/dist/server/async-storage/request-async-storage-wrapper.js:77:24)
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:206:75)
    at AsyncLocalStorage.run (node:async_hooks:330:14)
    at AppRouteRouteModule.execute (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:203:56)
    at AppRouteRouteModule.handle (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:326:41)
    at RouteHandlerManager.handle (/Users/foo/react-pdf-nextjs/node_modules/next/dist/server/future/route-handler-managers/route-handler-manager.js:28:29)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async doRender (/Users/foo/react-pdf-nextjs/node_modules/next/dist/server/base-server.js:996:38)
    at async cacheEntry.responseCache.get.incrementalCache.incrementalCache (/Users/foo/react-pdf-nextjs/node_modules/next/dist/server/base-server.js:1182:28)
    at async /Users/foo/react-pdf-nextjs/node_modules/next/dist/server/response-cache/index.js:99:36

To Reproduce

The full reproduction code can be found here

Desktop (please complete the following information):

  • OS: MacOS
  • Browser: chrome
  • React-pdf version: 3.1.12

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 26
  • Comments: 24 (3 by maintainers)

Commits related to this issue

Most upvoted comments

Utilizing @joshuajaco/react-pdf-renderer-bundled is effective, but it would be great if these issues were addressed in the official repository.

Adding this to next.config.js solves issue for me:

const nextConfig = {
  experimental: {
    serverComponentsExternalPackages: ['@react-pdf/renderer'],
  }
};

Fantastic! I think this issue can be pretty much closed thanks to the above.

The issue seems to be that next bundles everything in the app directory using “react-server” mode where react only exports a subset of its functionality . This subset does not include the Component export which react-reconciler tries to access, leading to the above mentioned error.

As a temporary workaround I’ve published a fork that prebundles react, circumventing the problematic behavior.

Fixed in Next.js 14.1.1-canary.32 (available tomorrow) 🎉

@charanjit-singh It is working for me.

import {
  Document,
  Page,
  renderToBuffer,
} from "@joshuajaco/react-pdf-renderer-bundled";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const buffer = await renderToBuffer(
    <Document>
      <Page size="A4"></Page>
    </Document>,
  );

  return new NextResponse(buffer, {
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": `attachment; filename="app.pdf"`,
    },
  });
}

@joshuajaco this is an issue of Next.js 13 or React? I wish we can mentioned the fork in the quick start guide to make it clear to the developers developing Next.js application.

and this should be mentioned in the documentation?

The reason you’re getting ba.Component() is not a constructor is because Next.js opted for a (ridiculous) decision to transparently replace your version of React with 18.3.0-canary-8c8ee9ee6-20231026 (sic!!!) if you happen to use App Router. This version notably missed React.Component.

To fix this issue on our side, without bundling React, we would need to drop react-reconciler dependency altogether or… update it to 0.30.0-canary, but this brings in a couple of breaking changes we would need to deal with.

I made @react-pdf/renderer works on app route page adding 'use client' on top of page and adding @react-pdf/renderer to transpilePackages in next.config.js

const nextConfig = {
  transpilePackages: ['@react-pdf/renderer'],
}

Any updates yet?

Any luck addressing this issue?

next 14.2 is out now, seems like rendering pdf in app-folder route works out of the box 🎉

Adding this to next.config.js solves issue for me:

const nextConfig = {
  experimental: {
    serverComponentsExternalPackages: ['@react-pdf/renderer'],
  }
};

Getting this error when I try to include a image in a PDF and it work fine with @joshuajaco/react-pdf-renderer-bundled. Not sure which other packages need to be included in the serverComponentsExternalPackages

node_modules/yoga-layout/binaries/wasm-async-node.js:11
process.on("uncaughtException",function(a){throw a;});process.on("unhandledRejection",function(a){throw a;});g.inspect=function(){return"[Emscripten Module object]"};var ia=g.print||console.log.bind(console),v=g.printErr||console.warn.bind(console);Object.assign(g,ba);ba=null;var ja;g.wasmBinary&&(ja=g.wasmBinary);var noExitRuntime=g.noExitRuntime||!0;"object"!=typeof WebAssembly&&w("no native wasm support detected");var ka,la=!1;
                                           ^

TypeError: PNG is not a constructor
    at new PNGImage

Will this be solved?

For anyone stumbling upon this since Next.js 13 changed the route handlers: I was able to get this running in a Next.js route using:

export const GET: NextApiHandler = async (req, res) => {

  const pdfStream: NodeJS.ReadableStream = await ReactPDF.renderToStream(
    <MyDocument />
  );
  const webStream = Readable.toWeb(new Readable().wrap(pdfStream));
  return new Response(webStream as ReadableStream, {
    headers: { "Content-Type": "application/pdf" },
  });
};

Not sure if this is the best way, happy to hear about better ways to do this!

Didn’t work

I have created page api, in Next Js even when you use App Router you can create Page Api Folder and there it works.

pages/api/ - it’s my solution even in App Router