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

Created on 24 May 2019  路  8Comments  路  Source: diegomura/react-pdf

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;

Most helpful comment

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> 
      )}
    </>
  );
}

All 8 comments

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.

Thank you so much. :)

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.

Can anyone share an example of a fix for the original issue here, to get PDFDownloadLink working in Nextjs? @diegomura @hwbell @iACM1994

I tried setting ssr to false for the imported PdfDocument component, as well as for the import of PDFDownloadLink from lib and I keep getting the same error complaining of an object in a LoadedComponent

My current code:

import React from 'react'
import Link from 'next/link'
import dynamic from 'next/dynamic'
import { Page } from '../../components/Global'
import LessonSteps from '../lesson/LessonSteps'
import "isomorphic-fetch"
import dateFormat from 'dateformat'
import axios from 'axios'
const PDFDownloadLink = dynamic(() => import('@react-pdf/renderer'), {
  ssr: false
});
const PdfDocument = dynamic(() => import('../../components/PdfDocument'), {
  ssr: false
});

...

class Lesson extends React.Component {
    static async getInitialProps ({ query }) {
      const res = await fetch('https://secret')
      const json = await res.json()
      return { 
        lesson: json.data.filter((e)=>
          {
            return e.id === query.id
          })[0] //because filter returns an array, 
                //and we need an object to grab parts, I call for 0th item in array
      }
    }

    render() {
      return (
        <Page>
...
                <PDFDownloadLink {<PdfDocument {...this.props.lesson} >} fileName="lesson.pdf">
                  {({ blob, url, loading, error }) => ('Download A Preview')}
                </PDFDownloadLink>
...

The error in full:

:3000/_next/static/runtime/main.js?ts=1564512017734:10866 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

Check the render method of `LoadableComponent`.
    in LoadableComponent (at lesson/index.js:250)
    in div (at lesson/index.js:241)
    in div (at lesson/index.js:80)
    in div (at lesson/index.js:69)
    in PageWrapper (at lesson/index.js:54)
    in Lesson (created by App)
    in Container (created by App)
    in App
    in Suspense

Alternate error, when PDFDownloadLink isn't dynamically imported:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
    at invariant (/Users/theresa/lesson-admin/node_modules/react-dom/cjs/react-dom-server.node.development.js:58:15)
    at ReactDOMServerRenderer.render (/Users/theresa/lesson-admin/node_modules/react-dom/cjs/react-dom-server.node.development.js:3443:7)
    at ReactDOMServerRenderer.read (/Users/theresa/lesson-admin/node_modules/react-dom/cjs/react-dom-server.node.development.js:3161:29)
    at renderToString (/Users/theresa/lesson-admin/node_modules/react-dom/cjs/react-dom-server.node.development.js:3646:27)
    at render (/Users/theresa/lesson-admin/node_modules/next-server/dist/server/render.js:86:16)
    at renderPage (/Users/theresa/lesson-admin/node_modules/next-server/dist/server/render.js:211:20)
    at Function.value (/Users/theresa/lesson-admin/.next/server/static/development/pages/_document.js:555:41)
    at Object.loadGetInitialProps (/Users/theresa/lesson-admin/node_modules/next-server/dist/lib/utils.js:42:35)
    at Object.renderToHTML (/Users/theresa/lesson-admin/node_modules/next-server/dist/server/render.js:218:36)
    at process._tickCallback (internal/process/next_tick.js:68:7)

NB I've tried some permutations to pinpoint the issue:

  • render without passing in the pdf page component and/or the filename - returns same error
  • render just the imported component for the pdf I made straight into the page (ie not calling ); this works fine

Thanks!

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.

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'm having the same issues on next.js 9.0.5, @alexcaulfield 's solution works for me. Using dynamic imports/components gives me the same errors as above.

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

jest.mock('@react-pdf/renderer', () => ({
  PDFDownloadLink: () => null,
  Font: {
    register: () => null
  },
  StyleSheet: {
    create: () => null
  }
}));
Was this page helpful?
0 / 5 - 0 ratings