react-pdf: PDFDownloadLink work only work when render but hit error when reload page

Hi, I am trying to use PDFDownloadLink to create download pdf link in my react project. It works after render but after reload the page, it hit error and I don’t know why.

I am using react with next js and typescript.

Error is :

PDFDownloadLink is a web specific API. Or you're either using this component on Node, or your bundler is not loading react-pdf from the appropiate web build.

This is main template .tsx

import React from 'react'
import { Row, Col, Carousel } from 'antd'
import { PDFDownloadLink, Document, Page } from '@react-pdf/renderer'

import MyDocument from '../components/tour-itinerary-pdf'

export default class extends React.PureComponent<{tour: TcResponse<Tour>, cart: Cart}> {

	render() {
		const Print = () => (
			<div>
				<PDFDownloadLink document={<MyDocument />} fileName="somename.pdf">
			  		{({ loading }) => (loading ? 'Loading document...' : 'Download now!')}
				</PDFDownloadLink>
		      	</div>
		)

		return(
			<Row>
			  	<Col>
					{Print()}
				</Col>
			</Row>
		)
	}
}

This is component template

import React from 'react';
import { Page, Text, View, Document, StyleSheet } from '@react-pdf/renderer';


// Create styles
const styles = StyleSheet.create({
    page: {
        flexDirection: 'row',
        backgroundColor: '#E4E4E4'
    },
    section: {
        margin: 10,
        padding: 10,
        flexGrow: 1
    }
});

// Create Document Component
class MyDocument extends React.Component{
    render(){
        return(
            <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>
        )
    }
}

export default MyDocument;

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 16 (1 by maintainers)

Most upvoted comments

I’m not using Nextjs but instead of doing the dynamic import, I use hooks in my component to prevent the PDFDownloadLink from SSRing

const ResumeContainer = () => {
  const [isClient, setIsClient] = useState(false)

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

  return (
    <>
      {isClient && (
        <PDFDownloadLink document={
          <PdfDocument 
            headerNodes={data.allGoogleSheetHeaderRow.edges}
          />
        } fileName="resume.pdf">
          {({ blob, url, loading, error }) => (loading ? 'Loading document...' : 'Download my resume')}
        </PDFDownloadLink> 
      )}
    </>
  );
}

I fixed the issue by implementing Lazy Loading. With NextJS 13.4 now stable, React-PDF should update their documentation to help NextJs users.

"use client";
import dynamic from "next/dynamic";
const PDFDownloadLink = dynamic(() => import("@react-pdf/renderer").then((mod) => mod.PDFDownloadLink), {
  ssr: false,
  loading: () => <p>Loading...</p>,
});
import PdfDocument from "@/components/PdfDocument";

export default function PDF() {
  return (
    <div>
      <PDFDownloadLink document={<PdfDocument />} fileName="somename.pdf">
        {({ loading }) => (loading ? "Loading document..." : "Download now!")}
      </PDFDownloadLink>
    </div>
  );
}

React PDF now ships with a usePDF hook permanently. You can use it and it works fine in Next.js

import { usePDF, Document, Page } from '@react-pdf/renderer';

const MyDoc = (
  <Document>
    <Page>
      // My document data
    </Page>
  </Document>
);

const App = () => {
  const [instance, updateInstance] = usePDF({ document: MyDoc });

  if (instance.loading) return <div>Loading ...</div>;

  if (instance.error) return <div>Something went wrong: {error}</div>;

  return (
    <a href={instance.url} download="test.pdf">
      Download
    </a>
  );
}

I was getting the PDFDownloadLink is a web specific API. Or you're either using this component on Node, or your bundler is not loading react-pdf from the appropiate web build. error during snapshot testing similar to @hwbell. I’m working around this by mocking the components for now:

jest.mock('@react-pdf/renderer', () => {
  return {
    PDFDownloadLink: () => null,
    StyleSheet: {
      create: () => null
    }
  };
});

This is a pretty naive implementation, but resolves the error for me.

React PDF now ships with a usePDF hook permanently. You can use it and it works fine in Next.js

import { usePDF, Document, Page } from '@react-pdf/renderer';

const MyDoc = (
  <Document>
    <Page>
      // My document data
    </Page>
  </Document>
);

const App = () => {
  const [instance, updateInstance] = usePDF({ document: MyDoc });

  if (instance.loading) return <div>Loading ...</div>;

  if (instance.error) return <div>Something went wrong: {error}</div>;

  return (
    <a href={instance.url} download="test.pdf">
      Download
    </a>
  );
}

I literally copy and pasted this example but this is not working for me. Using version 2.0.20. It stuck on “Loading…” forever… any ideas why?

This happens when next.js renders the page server side. You cannot do that, since what <PDFDownloadLink /> does is creating a blob file on the browser with the PDF content. You have 2 options:

1 - Always generating the PDF on the server: on server side rendering, you generate the doc and embebed on your page as a base64 file or smth. Never did this, and you will still have to resolve client side rendering 2 - Use next js dynamic import with ssr disabled: This would be my way to go, and it’s actually how react-pdf playground is built.

@shivpratik the above implementation do not work for me , can any one help me out ?.

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

const QuestionsPage = () => {
  const { questions } = useQuestions();

  return (
    <PDFDownloadLink document={<PDF questions={questions} />} fileName="somename.pdf">
      {({ loading }) => (loading ? "Loading document..." : "Download now!")}
    </PDFDownloadLink>
  )
}

I get this error as well, but only when running tests with jest in my react app on the component that renders the pdf. It throws the error during a snapshot test:

it('renders correctly', async () => {
    const tree = await renderer
      .create(<CollectionPage  {...someProps}/>)
      .toJSON();
    expect(tree).toMatchSnapshot();
  });

The PDFDownloadLink is rendered within the CollectionPage. Any thoughts? Thanks in advance.

Thank you @benrobertsonio - that did the trick for me.

jest.mock('@react-pdf/renderer', () => ({
  PDFDownloadLink: () => null,
  Font: {
    register: () => null
  },
  StyleSheet: {
    create: () => null
  }
}));