Skip to content

Instantly share code, notes, and snippets.

@gitname
Last active June 10, 2024 09:20

Revisions

  1. gitname revised this gist Feb 10, 2023. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    # Using `@react-pdf/renderer` with React 18
    # Using `@react-pdf/renderer` v3.0.1 with React 18

    ## Introduction

    When I tried to use the `@react-pdf/renderer` package with a React 18 app, two problems arose. In this article, I'll describe those problems and tell you how I solved them.
    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

  2. gitname revised this gist Dec 16, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,8 @@

    When I tried to use the `@react-pdf/renderer` package 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

    ## Problem 1: Dependency Conflict

    According to its [`package.json`](https://github.com/diegomura/react-pdf/blob/b385dcbc9cf90254bbdafec161e51087249b20fa/packages/renderer/package.json#L40) 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.
  3. gitname revised this gist Dec 16, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -73,11 +73,11 @@ TS2769: No overload matches this call.
    Property 'children' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Svg> & Readonly<SVGProps>'
    ```

    ### Solution: Restore `children` prop
    ### Solution: Use custom components

    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 (I, personally, put it in a new file at `src/patches/@react-pdf/renderer/index.tsx`):
    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`):

    ```tsx
    import ReactPDF from '@react-pdf/renderer';
  4. gitname revised this gist Dec 16, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -49,7 +49,7 @@ $ npm install @react-pdf/renderer

    **Note:** If you are not using TypeScript, you can ignore this problem.

    The [type definitions of some components](https://github.com/diegomura/react-pdf/blob/master/packages/renderer/index.d.ts#L247) 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 [type definitions of some components](https://github.com/diegomura/react-pdf/blob/b385dcbc9cf90254bbdafec161e51087249b20fa/packages/renderer/index.d.ts#L247) 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](https://solverfox.dev/writing/no-implicit-children/) in React 18. However, the type definitions in the `@react-pdf/renderer` package have not been updated accordingly.

  5. gitname created this gist Dec 16, 2022.
    121 changes: 121 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,121 @@
    # Using `@react-pdf/renderer` with React 18

    ## Introduction

    When I tried to use the `@react-pdf/renderer` package with a React 18 app, two problems arose. In this article, I'll describe those problems and tell you how I solved them.

    ## Problem 1: Dependency Conflict

    According to its [`package.json`](https://github.com/diegomura/react-pdf/blob/b385dcbc9cf90254bbdafec161e51087249b20fa/packages/renderer/package.json#L40) 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**:

    ```shell
    $ 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
    ```

    ### Solution A: Use `--legacy-peer-deps` option

    **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`.

    ```shell
    $ npm install @react-pdf/renderer --legacy-peer-deps
    ```

    ### Solution B: Use `overrides` property

    A different workaround—and the one I prefer—is to use the [`overrides`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#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.

    ```json
    "overrides": {
    "@react-pdf/renderer": {
    "react": "^18.0.0"
    }
    },
    ```

    ```shell
    $ npm install @react-pdf/renderer
    ```

    ## Problem 2: Inaccurate TypeScript types

    **Note:** If you are not using TypeScript, you can ignore this problem.

    The [type definitions of some components](https://github.com/diegomura/react-pdf/blob/master/packages/renderer/index.d.ts#L247) 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](https://solverfox.dev/writing/no-implicit-children/) 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...

    ```tsx
    import { Svg } from "@react-pdf/renderer";

    const MyComponent = () => (
    <Svg>
    {/* child elements go here */}
    </Svg>
    );
    ```

    ...results in a **compiler error**:

    ```shell
    TS2769: No overload matches this call.
    ...
    Property 'children' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<Svg> & Readonly<SVGProps>'
    ```

    ### Solution: Restore `children` prop

    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 (I, personally, put it in a new file at `src/patches/@react-pdf/renderer/index.tsx`):

    ```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:

    ```diff
    import {
    Svg
    - } from "@react-pdf/renderer";
    + } from "../patches/@react-pdf/renderer";

    const MyComponent = () => (
    <Svg>
    {/* child elements go here */}
    </Svg>
    );
    ```

    ## Conclusion

    With the above solutions in place, I use `@react-pdf/renderer` with React 18.