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