When I tried to use the @react-pdf/renderer
package (version 3.0.1
) with a React 18 app, two problems arose. In this article, I'll describe those problems and tell you how I solved them.
Update: Here's a video demonstration of the problems and solution described in this article: https://youtu.be/YZP5r7Uy_bU
According to its package.json
file, the @react-pdf/renderer
package (as of version 3.0.1
—the latest version) is only compatible with React versions 16 and 17. Meanwhile, React apps created using create-react-app
(as of version 5.0.1
—the latest version) run React version 18.
Trying to install the @react-pdf/renderer
package into such an app results in a dependency conflict:
$ npm install @react-pdf/renderer
...
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.6 || ^17.0.0" from @react-pdf/[email protected]
npm ERR! node_modules/@react-pdf/renderer
npm ERR! @react-pdf/renderer@"*" from the root project
Note: I recommend Solution B. I am only documenting Solution A for reference, since I have seen several people propose it.
One workaround people have proposed is that developers use the --legacy-peer-deps
option when installing the @react-pdf/renderer
package. I don't like this approach because (a) it requires me to include that command-line option every time I install any package after that; and (b) it skips peer dependency checks for all packages, not just for @react-pdf/renderer
.
$ npm install @react-pdf/renderer --legacy-peer-deps
A different workaround—and the one I prefer—is to use the overrides
property in my app's package.json
file. This tells NPM that @react-pdf/renderer
really depends upon React 18. Unlike with the other workaround; here, (a) the "override" remains documented in package.json
and (b) only the @react-pdf/renderer
package and its descendants are affected.
"overrides": {
"@react-pdf/renderer": {
"react": "^18.0.0"
}
},
$ npm install @react-pdf/renderer
Note: If you are not using TypeScript, you can ignore this problem.
The type definitions of some components in the @react-pdf/renderer
package were written under the assumption that any component whose type was React.Component
would implictly accept a children
prop. Some people refer to that implict acceptance of a children
prop as "implicit children."
The "implicit children" behavior was removed from React in React 18. However, the type definitions in the @react-pdf/renderer
package have not been updated accordingly.
As a result, trying to render any of the affected components with child elements, like this...
import { Svg } from "@react-pdf/renderer";
const MyComponent = () => (
<Svg>
{/* child elements go here */}
</Svg>
);
...results in a compiler error:
TS2769: No overload matches this call.
...
Property 'children' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Svg> & Readonly<SVGProps>'
To work around that, I define and use custom variants of the affected components. These custom variants do accept a children
prop.
To define the custom variants, I copy/paste the following code into any .tsx
file in my app's src/
folder (personally, I put it in a new file at src/patches/@react-pdf/renderer/index.tsx
):
import ReactPDF from '@react-pdf/renderer';
import { FC, PropsWithChildren } from 'react';
// Custom variants of `@react-pdf/renderer` components, which accept a `children` prop.
//
// Credits: Special thanks to GitHub user @antoineharel for sharing this solution at:
// https://github.com/diegomura/react-pdf/pull/1798#issuecomment-1259552615
export const Svg: FC<PropsWithChildren<ReactPDF.SVGProps>> = ({ ...props }) => (
<ReactPDF.Svg {...props} />
);
export const G: FC<PropsWithChildren<ReactPDF.GProps>> = ({ ...props }) => (
<ReactPDF.G {...props} />
);
export const ClipPath: FC<PropsWithChildren<ReactPDF.ClipPathProps>> = ({ ...props }) => (
<ReactPDF.ClipPath {...props} />
);
Now, whenever I would normally import any of the affected components, I import the custom variant instead of the original @react-pdf/renderer
variant:
import {
Svg
- } from "@react-pdf/renderer";
+ } from "../patches/@react-pdf/renderer";
const MyComponent = () => (
<Svg>
{/* child elements go here */}
</Svg>
);
With the above solutions in place, I use @react-pdf/renderer
with React 18.
Hi @mdtaju, I have not come across that error. I have used the workaround (with option "b") in both development and production without issue. I wonder whether the error you encountered is due to some other package you are using. I have some suggestions:
@react-pdf/renderer
works in both your development and production environment. You can follow along with the video linked in the Gist in order to create the bare-bones app.