Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: PDFViewer is a web specific API. #2599

Closed
gokhangunduz opened this issue Feb 5, 2024 · 24 comments
Closed

Error: PDFViewer is a web specific API. #2599

gokhangunduz opened this issue Feb 5, 2024 · 24 comments

Comments

@gokhangunduz
Copy link

Hello everyone, first of all I would like to thank everyone who contributed to this functional project. Coming to problem, I have a problem that I cannot solve;

In my Next.JS project, I get an error when I want to render my component named <Document/> in <PDFViewer/>.

Currently, I cannot perform dynamic operations on PDF.

After the reload process, the error is displayed on the node-console.

Note: I am currently doing Client Side Rendering of all components in my Next.JS project.

  • OS: MacOS
  • Browser: Safari
  • React-pdf version: 3.3.5

Preview.tsx;

"use client";

import { PDFViewer } from "@react-pdf/renderer";
import { ReactElement } from "react";
import Document from "@/components/Document/Document";

export default function Preview(): ReactElement {
  return (
    <div className="h-full w-full">
      <PDFViewer className="h-full w-full" showToolbar={false}>
        <Document />
      </PDFViewer>
    </div>
  );
}

Document.tsx;

"use client";

import React, { ReactElement } from "react";
import {
  Page,
  Text,
  View,
  Document as PDF,
  StyleSheet,
} from "@react-pdf/renderer";

export default function Document(): ReactElement {
  const styles = StyleSheet.create({
    page: {
      flexDirection: "row",
      backgroundColor: "#FFF",
    },
    section: {
      margin: 10,
      padding: 10,
      flexGrow: 1,
    },
  });

  return (
    <PDF>
      <Page size="A4" style={styles.page}>
        <View style={styles.section}>
          <Text>Section #1</Text>
        </View>
        <View style={styles.section}>
          <Text>Section #2</Text>
        </View>
      </Page>
    </PDF>
  );
}

Bash;

✓ Compiled in 299ms (1272 modules)
 ⚠ Fast Refresh had to perform a full reload. Read more: https://nextjs.org/docs/messages/fast-refresh-reload
 ⨯ node_modules/@react-pdf/renderer/lib/react-pdf.js (4446:8) @ throwEnvironmentError
 ⨯ Internal error: Error: PDFViewer is a web specific API. You're either using this component on Node, or your bundler is not loading react-pdf from the appropriate web build.
    at throwEnvironmentError (./node_modules/@react-pdf/renderer/lib/react-pdf.js:4386:11)
    at PDFViewer (./node_modules/@react-pdf/renderer/lib/react-pdf.js:4392:5)
@gokhangunduz
Copy link
Author

Hello again, I think I have narrowed down the source of the problem somewhat. While I have been trying to solve this problem for a long time, I think the source of the entire problem is in the <PDFViewer /> component.

Because when I do not use this component, the error disappears. Node-console does not give any errors and the pdf can become dynamic. I need some help on the exact cause or solution to the problem. Do you have any solution or idea about the problem? @diegomura :(

@wojtekmaj
Copy link
Contributor

wojtekmaj commented Feb 6, 2024

Try following instructions in https://react-pdf.org/compatibility. I'm not sure it's gonna help, but it won't hurt either :D

@gokhangunduz
Copy link
Author

Hello again, unfortunately the problem is not solved :( I wonder if the problem will be solved if I switch to the pure React project instead of Next.

@jd-carroll
Copy link

@gokhangunduz This is your issue: #2624

@deonneon
Copy link

deonneon commented Feb 9, 2024

This will fix it for you. Use Next dynamic function to manually set server side rendering off and import it through that function instead of the regular import

"use client";

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
  () => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
  {
    ssr: false,
    loading: () => <p>Loading...</p>,
  },
);

instead of

"use client";

import { PDFViewer } from "@react-pdf/renderer";

@gokhangunduz
Copy link
Author

This will fix it for you. Use Next dynamic function to manually set server side rendering off and import it through that function instead of the regular import

"use client";

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
  () => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
  {
    ssr: false,
    loading: () => <p>Loading...</p>,
  },
);

instead of

"use client";

import { PDFViewer } from "@react-pdf/renderer";

I am grateful for your interest and comment. My problem is solved. Thank you so much bro.

@vl4di99
Copy link

vl4di99 commented Feb 12, 2024

What if we use Remix JS? The same error is thrown
image

@lou
Copy link

lou commented Feb 21, 2024

I also use Remix and can't make it work.
I came up with the following solution, trying to load the component on the client only, but the error persists.

// ParentComponent.tsx
import React, { lazy, Suspense } from "react";

const PdfPreview = lazy(async () => {
  const module = await import("./PdfPreview.client");
  return { default: module.default };
});

const ParentComponent = () => {
  return (
    <div>
      <Suspense fallback={<div>Loading preview...</div>}>
        <PdfPreview />
      </Suspense>
     </div>
  );
}
// PdfPreview.client.tsx
import { Document, Page, Text, View, StyleSheet, PDFViewer } from "@react-pdf/renderer";

const styles = StyleSheet.create({
  page: {
    flexDirection: "row",
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1,
  },
});

const PdfPreview = () => {
  return (
    <PDFViewer>
      <Document>
        <Page size="A4" style={styles.page}>
          <View style={styles.section}>
            <Text>Section #1</Text>
          </View>
          <View style={styles.section}>
            <Text>Section #2</Text>
          </View>
        </Page>
      </Document>
    </PDFViewer>
  );
};

export default PdfPreview;

@John-Dennehy
Copy link

Try following instructions in https://react-pdf.org/compatibility. I'm not sure it's gonna help, but it won't hurt either :D

That section is really of no help at all. Firstly, the latest (stable) version of Next.js is 14.1.0 so pointing to 14.1.1 isn't very helpful. Second, the other 'fix' doesn't work either.

I would suggest that whole section on Next be replaced with the following solution posted above:

This will fix it for you. Use Next dynamic function to manually set server side rendering off and import it through that function instead of the regular import

"use client";

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
  () => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
  {
    ssr: false,
    loading: () => <p>Loading...</p>,
  },
);

instead of

"use client";

import { PDFViewer } from "@react-pdf/renderer";

@ChuzOP
Copy link

ChuzOP commented Mar 11, 2024

This will fix it for you. Use Next dynamic function to manually set server side rendering off and import it through that function instead of the regular import

"use client";

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
  () => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
  {
    ssr: false,
    loading: () => <p>Loading...</p>,
  },
);

instead of

"use client";

import { PDFViewer } from "@react-pdf/renderer";

That works for me in "next": "14.1.2"

@nikita-pankratov
Copy link

@lou did you get it to work with Remix? I'm struggling with the same error.

@SamucaDev
Copy link

import dynamic from "next/dynamic";

const PDFViewer = dynamic(
() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
{
ssr: false,
loading: () =>

Loading...

,
},
);

Is the problem that the page can't finish importing before the server finishes rendering?

or

PDFviewer couldn't be ssr

I want to understand why it solves the problem..

@gokhangunduz
Copy link
Author

import dynamic from "next/dynamic";
const PDFViewer = dynamic(
() => import("@react-pdf/renderer").then((mod) => mod.PDFViewer),
{
ssr: false,
loading: () => Loading...,
},
);

Is the problem that the page can't finish importing before the server finishes rendering?

or

PDFviewer couldn't be ssr

I want to understand why it solves the problem..

The PDFViewer object SSR cannot run, hence the error thrown is this: PDFViewer is a web specific API. With the dynamic package in Next.js, the package to be imported is guaranteed to work on the CSR side.

In this way, it can communicate with the web-APIs in the package to be imported.

@cameronkloot
Copy link

@nikita-pankratov I had the same "PDFViewer is a web specific API" when running my Remix project in production on Vercel, but not while developing locally. I didn't have the time to dig into why the node renderer was being imported instead of the dom renderer, but I was able to get around the issue by building the project locally and including react-pdf.browser.js directly in my project. I also had to include my fonts in the manually built .js file.

react-pdf.browser.js:

...
var render = function render() {
  throwEnvironmentError('render');
};
Font.registerHyphenationCallback((word) => [word])
Font.register({
  family: 'Montserrat',
  src: 'https://storage.googleapis.com/resimplifi-associate/fonts/Montserrat/Montserrat-Medium.ttf',
})
// TODO: remove this default export in next major release because it breaks tree-shacking
var index = _extends({
  pdf: pdf,
...

Here's the component I use instead of using PDFViewer directly:

PdfClientBrowser.client.tsx:

import React from 'react'
import { ClientOnly } from '~/components'

const PDFViewer = React.lazy(() =>
  import('~/react-pdf.browser.js').then((module) => ({
    default: module.PDFViewer,
  }))
)

export default function PdfClientBrowser({
  document,
}: {
  document: React.ReactElement
}) {
  return (
    <ClientOnly>
      <PDFViewer height="100%" width="100%" className="absolute inset-0">
        {document}
      </PDFViewer>
    </ClientOnly>
  )
}

At some point, I'll see if I can debug properly, but this works for now.

@rtnAyush
Copy link

rtnAyush commented Jul 1, 2024

Yes that works, but there is a cache, I have app router and i imported the component that have complied pdf and inside that i have mark use client,

When i used dynamic import my problem still not get solved(still getting that error)

Then i have imported the whole pdf client component use dynamic import inside page.tsx file, then it got solved.

  import { fetchSomething } from '@/lib/controllers/something.controllers';
  import { ISomething  } from '@/types';
  import dynamic from 'next/dynamic';
  const Something PDF = dynamic(() => import("@/components/something/pdf/Something PDF"), {
      loading: () => <p>Loading...</p>,
      ssr: false,
  });
  
  export default async function page({ params }: { params: { somethingId: string } }) {
      // fetch Something  data
      const something : ISomething  = await fetchSomething(params.somethingId);
  
      return (
            // use client component
          <SomethingPDF something={something} />
      )
  }

@njoshi22
Copy link

This is actually still not working for me even with dynamic - this is on Next 14.2.4 and 14.2.5.

@sunflowerm
Copy link

For anyone still struggling on NextJS, this works for me. I use useEffect to simply conditionally render it when the page has loaded.

"next": "^14.2.1",
"@react-pdf/renderer": "^3.4.4",

import { useEffect } from "react"
import { PDFViewer, Document, Page, Text, View, StyleSheet } from "@react-pdf/renderer"

const Document = () => {
   const [loaded, setLoaded] = useState(false)

   useEffect(() => {
      setLoaded(true)
   }, [])

   return (
      <>
         {loaded && (
            <PDFViewer >
               <Document>
                  <Page size="A4" >
                     <Text ></Text>
                     <View ></View>
                  </Page>
               </Document>
            </PDFViewer>
         )}
      </>
   )
}

export default Document

@IgnacioNMiranda
Copy link

@sunflowerm I was blessed by needing a solution and you posting it 11 hours ago. It works flawlessly. Thanks.

@makkarankush68
Copy link

For anyone still struggling on NextJS, this works for me. I use useEffect to simply conditionally render it when the page has loaded.

"next": "^14.2.1", "@react-pdf/renderer": "^3.4.4",

import { useEffect } from "react"
import { PDFViewer, Document, Page, Text, View, StyleSheet } from "@react-pdf/renderer"

const Document = () => {
   const [loaded, setLoaded] = useState(false)

   useEffect(() => {
      setLoaded(true)
   }, [])

   return (
      <>
         {loaded && (
            <PDFViewer >
               <Document>
                  <Page size="A4" >
                     <Text ></Text>
                     <View ></View>
                  </Page>
               </Document>
            </PDFViewer>
         )}
      </>
   )
}

export default Document

bro is the hero

@jpainam
Copy link

jpainam commented Sep 29, 2024

Having this issue. anyone knows how to solve it?

Module not found: ESM packages (@react-pdf/renderer) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals

https://nextjs.org/docs/messages/module-not-found

@nicholsss
Copy link

Having this issue. anyone knows how to solve it?

Module not found: ESM packages (@react-pdf/renderer) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals

https://nextjs.org/docs/messages/module-not-found

Were u able to fix this? I'm facing same issue

@bdlowery
Copy link

bdlowery commented Oct 13, 2024

Having this issue. anyone knows how to solve it?

Module not found: ESM packages (@react-pdf/renderer) need to be imported. Use 'import' to reference the package instead. https://nextjs.org/docs/messages/import-esm-externals

https://nextjs.org/docs/messages/module-not-found

Yeah same thing. I copied the code exactly like in #2599 (comment). It only happens when trying to dynamically import the PDFViewer on next 14.2.15

@jamalsoueidan
Copy link

For remix people, here is my version ( https://github.com/jamalsoueidan/cv-editor )

@marco-ka
Copy link

marco-ka commented Nov 5, 2024

For Remix: Updating to @react-pdf/renderer 4.0.0 solved this for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests