UNPKG

21.7 kBMarkdownView Raw
1<p align="center">
2 <img width="300" height="300" src="./logo.png" alt="react-to-print logo">
3</p>
4
5# ReactToPrint - Print React components in the browser
6
7[![NPM Downloads](https://img.shields.io/npm/dt/react-to-print.svg?style=flat)](https://npmcharts.com/compare/react-to-print?minimal=true)
8[![npm version](https://badge.fury.io/js/react-to-print.svg)](https://badge.fury.io/js/react-to-print)
9
10Print the content of a React component.
11
12## Demo
13
14[![Run react-to-print](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/rzdhd)
15
16## Install
17
18`npm install --save react-to-print`
19
20## API
21
22### Usage
23
24```tsx
25const contentRef = useRef<HTMLDivElement>(null);
26const reactToPrintFn = useReactToPrint({ contentRef });
27
28return (
29 <div>
30 <button onClick={reactToPrintFn}>Print</button>
31 <div ref={contentRef}>Content to print</div>
32 </div>
33);
34```
35
36### Options
37
38| Option | Type | Description |
39| :-------------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------- |
40| **`bodyClass?`** | `string` | One or more class names to pass to the print window, separated by spaces |
41| **`contentRef?`** | `React.RefObject<Element \| Text>` | The ref pointing to the content to be printed. Alternatively, pass the ref directly to the callback returned by `useReactToPrint` |
42| **`documentTitle?`** | `string` | Set the title for printing when saving as a file. Ignored when passing a custom `print` option |
43| **`fonts?`** | `{ family: string, source: string; weight?: string; style?: string; }[]` | A list of fonts to load into the printing iframe. This is useful if you are using custom fonts |
44| **`ignoreGlobalStyles?`** | `boolean` | Ignore all `<style>` and `<link type="stylesheet" />` tags from `<head>` |
45| **`nonce?`** | `string` | Set the [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce) attribute for allow-listing script and style elements for Content Security Policy (CSP) |
46| **`onAfterPrint?`** | `() => void` | Callback function that triggers after the print dialog is closed _regardless of if the user selected to print or cancel_ |
47| **`onBeforePrint?`** | `() => Promise<void>` | Callback function that triggers before print. This can be used to change the content on the page before printing as an alternative to, or in conjunction with `@media print` queries |
48| **`onPrintError?`** | `(errorLocation: 'onBeforePrint' \| 'print', error: Error) => void` | Called if there is a printing error serious enough that printing cannot continue. Currently limited to Promise rejections in `onBeforePrint`, and `print`. Use this to attempt to print again. `errorLocation` will tell you where the Promise was rejected |
49| **`pageStyle?`** | `string` | `react-to-print` sets some basic styles to help improve page printing, notably, removing the header and footer that most browsers add. Use this to override these styles and provide your own |
50| **`preserveAfterPrint?`** | `boolean` | Preserve the print iframe after printing. This can be useful for debugging by inspecting the print iframe |
51| **`print?`** | `(iframe: HTMLIFrameElement) => Promise<void>` | If passed, this function will be used instead of `window.print` to print the content. Use this to print in non-browser environments such as Electron |
52| **`suppressErrors?`** | `boolean` | When passed, prevents `console` logging of errors |
53| **`copyShadowRoots?`** | `boolean` | When passed, shadow root content will be copied to print window. WARNING: Use with care if you print large documents. TreeWalker's are used to traverse source and target documents. |
54
55The hook returns a function that will initiate the print process when called. This function can also be optionally passed the `content` when called, allowing for its use in conditional rendering logic (where hooks are not allowed) and/or in non-React code such as a util function. See the repo examples for more.
56
57## Compatibility
58
59`react-to-print` should be compatible with most modern browsers.
60
61### Mobile Browsers in WebView
62
63While printing on mobile browsers generally works, printing within a WebView (when your page is opened by an app such as Facebook or Slack, but not by the full browser itself) is known to generally not work. Some WebViews don't make the correct API available. Others make it available but cause printing to no-op.
64
65We are actively researching resolutions to this issue, but it likely requires changes by Google/Chromium and Apple/WebKit. See [#384](https://github.com/MatthewHerbst/react-to-print/issues/384) for more information. If you know of a way we can solve this your help would be greatly appreciated.
66
67### Known Incompatible Browsers
68
69- Firefox Android (does not support [`window.print`](https://developer.mozilla.org/en-US/docs/Web/API/Window/print))
70
71## Known Issues
72
73- Some mobile browser may, instead of printing, open the native Share action instead
74- `onAfterPrint` may fire immediately (before the print dialog is closed) on newer versions of Safari where [`window.print`](https://developer.mozilla.org/en-US/docs/Web/API/Window/print) does not block
75- ([401](https://github.com/MatthewHerbst/react-to-print/issues/401)): TypeScript errors such as `Type 'undefined' is not assignable to type 'ReactInstance | null'.`. You likely need to set your ref to initially be `null`: `useRef(null)`
76
77## Common Pitfalls
78
79- `documentTitle` will not work if `react-to-print` is run within an `iframe`. If `react-to-print` is run within an `iframe` and your script has access to the parent document, you may be able to manually set and then restore the parent document's `title` during the print. This can be done by leveraging the `onBeforePrint` and `onAfterPrint` callbacks.
80
81- When printing, only styles that directly target the printed nodes will be applied as the parent nodes of the printed nodes will not exist in the print DOM. For example, in the code below, if the `<p>` tag is the root of the `ComponentToPrint` then the red styling will *not* be applied. Be sure to target all printed content directly and not from unprinted parents.
82
83 ```jsx
84 <div className="parent">
85 <p>Hello</p>
86 </div>
87 ```
88
89 ```css
90 div.parent p { color:red; }
91 ```
92
93- The `connect` method from `react-redux` returns a functional component that cannot be assigned a reference to be used within the `contentRef`. To use a component wrapped in `connect` within `contentRef`, create an intermediate component that simply renders your component wrapped in `connect`. See [280](https://github.com/MatthewHerbst/react-to-print/issues/280) for more.
94
95- When rendering multiple components to print, ensure each is passed a unique ref. Then, either use a unique `useReactToPrint` call for each component, or, using a single `useReactToPrint` call pass the refs at print-time to the printing function returned by the hook. If you share refs across components only the last component will be printed. See [323](https://github.com/MatthewHerbst/react-to-print/issues/323) for more.
96
97## FAQ
98
99### Can `react-to-print` be used to download a PDF without using the Print Preview window?
100
101No. We aren't able to print a PDF as we lose control once the print preview window opens. However, it should be very easy to use `react-to-print` to take the information you need and pass it to a library that can generate a PDF.
102
103```tsx
104const handlePrint = useReactToPrint({
105 ...,
106 print: async (printIframe: HTMLIframeElement) => {
107 // Do whatever you want here, including asynchronous work
108 await generateAndSavePDF(printIframe);
109 },
110});
111```
112
113For examples of how others have done this, see [#484](https://github.com/MatthewHerbst/react-to-print/issues/484)
114
115### Can `react-to-print` be used to change the settings within the print preview dialog?
116
117No. The [`window.print`](https://developer.mozilla.org/en-US/docs/Web/API/Window/print) API does not provide a way to change these settings. Only various CSS hints can be provided, with each browser potentially treating them differently.
118
119### Can the `ComponentToPrint` be a Class component?
120
121Not directly. To print a Class based component you will need to manually forward the `contentRef` as a prop:
122
123```tsx
124class ComponentToPrint extends Component {
125 render() {
126 return (
127 <div ref={this.props.innerRef}>
128 Print content
129 </div>
130 )
131 }
132}
133
134function App {
135 const contentRef = useRef(null);
136 const handlePrint = useReactToPrint({ contentRef });
137
138 return (
139 <div>
140 <button onClick={handlePrint}>Print</button>
141 <ComponentToPrint innerRef={contentRef} />
142 </div>
143 );
144}
145```
146
147### Why does `onAfterPrint` fire even if the user cancels printing
148
149`onAfterPrint` fires when the print dialog closes, regardless of why it closes. This is the behavior of the [`onafterprint` browser event](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onafterprint).
150
151### Why does `react-to-print` skip `<link rel="stylesheet" href="">` tags
152
153`<link>`s with empty `href` attributes are [invalid HTML](https://www.w3.org/TR/html50/document-metadata.html#attr-link-href). In addition, they can cause all sorts of [undesirable behavior](https://gtmetrix.com/avoid-empty-src-or-href.html). For example, many browsers - including modern ones, when presented with `<link href="">` will attempt to load the current page. Some even attempt to load the current page's parent directory.
154
155*Note*: related to the above, `img` tags with empty `src` attributes are also invalid, and we may not attempt to load them.
156
157### How do you make `ComponentToPrint` show only while printing
158
159If you've created a component that is intended only for printing and should not render in the parent component, wrap that component in a `div` with style set to `{ display: "none" }`, like so:
160
161```jsx
162<div style={{ display: "none" }}><ComponentToPrint ref={componentRef} /></div>
163```
164
165This will hide `ComponentToPrint` but keep it in the DOM so that it can be copied for printing.
166
167### Setting state in `onBeforePrint`
168
169Recall that setting state is asynchronous. As such, you need to pass a `Promise` and wait for the state to update.
170
171```tsx
172const [isPrinting, setIsPrinting] = useState(false);
173const printRef = useRef(null);
174
175// We store the resolve Promise being used in `onBeforePrint` here
176const promiseResolveRef = useRef(null);
177
178// We watch for the state to change here, and for the Promise resolve to be available
179useEffect(() => {
180 if (isPrinting && promiseResolveRef.current) {
181 // Resolves the Promise, letting `react-to-print` know that the DOM updates are completed
182 promiseResolveRef.current();
183 }
184}, [isPrinting]);
185
186const handlePrint = useReactToPrint({
187 content: () => printRef.current,
188 onBeforePrint: () => {
189 return new Promise((resolve) => {
190 promiseResolveRef.current = resolve;
191 setIsPrinting(true);
192 });
193 },
194 onAfterPrint: () => {
195 // Reset the Promise resolve so we can print again
196 promiseResolveRef.current = null;
197 setIsPrinting(false);
198 }
199});
200```
201
202Note: for Class components, just pass the `resolve` to the callback for `this.setState`: `this.setState({ isPrinting: false }, resolve)`
203
204### Changing print settings in the print dialog
205
206Unfortunately there is no standard browser API for interacting with the print dialog. All `react-to-print` is able to do is open the dialog and give it the desired content to print. We cannot modify settings such as the default paper size, if the user has background graphics selected or not, etc.
207
208### Printing `video` elements
209
210`react-to-print` tries to wait for `video` elements to load before printing but a large part of this is up to the browser. Further, the image displayed will usually be the first frame of the video, which might not be what you expect to show. To ensure the proper image is displayed in the print we highly recommend setting the [`poster`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-poster) attribute of the `video`, which allows specifying an image to be a placeholder for the video until the video loads.
211
212### Electron
213
214`react-to-print` can be used for printing in Electron, but you will need to provide your own `print` method since Electron does not natively support the `window.print` method. Please see [this answer on StackOverflow](https://stackoverflow.com/a/70534565/2518231) for how to do this.
215
216There is a fully-working example of how to use `react-to-print` with Electron available [here](https://github.com/MatthewHerbst/electron-react-to-print-demo).
217
218### `link` elements not displaying styles properly
219
220Some frameworks such as Ruby on Rails will set `media="screen"` on `<link>` elements that don't have `screen` set. This can cause styles to appear incorrectly when printing. To fix, explicitly set `media="screen"` on your `<link>` elements. For `<link>` elements meant to apply only when printing, set `media="print"`.
221
222## Helpful Style Tips
223
224### Set the page orientation
225
226While you should be able to place these styles anywhere, sometimes the browser doesn't always pick them up. To force orientation of the page you can include the following in the component being printed:
227
228```jsx
229<style type="text/css" media="print">{"\
230 @page {\ size: landscape;\ }\
231"}</style>
232```
233
234### Set the page size
235
236The default page size is usually A4. Most browsers do not allow JavaScript or CSS to set the page size. For the browsers that do, it is usually done using the CSS page [`size`](https://developer.mozilla.org/en-US/docs/Web/CSS/@page/size) property. Check [`caniuse`](https://caniuse.com/mdn-css_at-rules_page_size) to see if the browsers you develop against support this.
237
238```css
239@media print {
240 @page {
241 size: 50mm 150mm;
242 }
243}
244```
245
246### Set custom margin to the page ([29](https://github.com/MatthewHerbst/react-to-print/issues/29))
247
248To set custom margin to the page,
249
250First, create a function to return the page margin,
251
252```js
253const getPageMargins = () => {
254 return `@page { margin: ${marginTop} ${marginRight} ${marginBottom} ${marginLeft} !important; }`;
255};
256```
257
258Now, within the JSX call this function within the style tags,
259
260```jsx
261<style>{getPageMargins()}</style>
262```
263
264PS: This style tag should be inside the component that is being passed in as the content ref.
265
266### Set landscape printing ([240](https://github.com/MatthewHerbst/react-to-print/issues/240))
267
268In the component that is passed in as the content ref, add the following:
269
270```css
271@media print {
272 @page { size: landscape; }
273}
274```
275
276### Printing elements that are not displayed ([159](https://github.com/MatthewHerbst/react-to-print/issues/159))
277
278Instead of using `{ display: 'none'; }`, try using `{ overflow: hidden; height: 0; }`
279
280### Using `pageStyle`
281
282`pageStyle` should be a CSS string. For example: `".divider { break-after: always; }"`
283
284### Getting a blank page when printing
285
286Many have found setting the following CSS helpful. See [#26](https://github.com/MatthewHerbst/react-to-print/issues/26) for more.
287
288```css
289@media print {
290 html, body {
291 height: 100vh; /* Use 100% here to support printing more than a single page*/
292 margin: 0 !important;
293 padding: 0 !important;
294 overflow: hidden;
295 }
296}
297```
298
299Another thing to try, especially if you are seeing this issue on mobile browsers, is to set `preserveAfterPrint: true` as it's possible the browser is causing the print iframe to be removed before printing has completed.
300
301### Styles incorrect in print dialog when using grid system
302
303We often ([#327](https://github.com/MatthewHerbst/react-to-print/issues/327), [#343](https://github.com/MatthewHerbst/react-to-print/issues/343), [#382](https://github.com/MatthewHerbst/react-to-print/issues/382)) see issues reported where the developer is using Bootstrap or a similar grid system, and everything works great until the user goes to print and suddenly it seems the styles are off. We've found that often the issue is the grid library uses the smallest sized columns during printing, such as the `xs` size on Bootstrap's grid, a size developers often don't plan for. The simplest solution is to ensure your grid will adapt to this size appropriately, though this may not be acceptable since you may want the large view to print rather than the smaller view. Another solution is to [override the grid column definition](https://stackoverflow.com/questions/22199429/bootstrap-grid-for-printing/28152320). Some newer versions of libraries have specific tools for dealing with printing, for example, [Bootstrap 4's Display property](https://getbootstrap.com/docs/4.3/utilities/display/).
304
305### Page Breaks
306
307What to know:
308
309- [`break-inside`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-inside) (replaces [`page-break-inside`](https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-inside))
310- [`break-before`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-before) (replaces [`page-break-before`](https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before))
311- [`break-after`](https://developer.mozilla.org/en-US/docs/Web/CSS/break-after) (replaces [`page-break-after`](https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-after))
312
313#### Pattern for Page-Breaking Dynamic Content
314
315Define a page-break class to apply to elements which could be sensibly split into a page.
316
317```html
318<div className="print-container" style={{ margin: "0", padding: "0" }}>
319 {listOfContent.map(yourContent => (
320 <>
321 <div className="page-break" />
322 <div>{yourContent}</div>
323 </>
324 )}
325</div>
326```
327
328In your styles, define your `@media print` styles, which should include setting your preference for CSS `page-break-` (see [w3's reference](https://www.w3schools.com/cssref/pr_print_pageba.asp) for options) to `auto`, and ensuring that your `page-break` element does not affect non-print styles.
329
330```css
331@media all {
332 .page-break {
333 display: none;
334 }
335}
336
337@media print {
338 html, body {
339 height: initial !important;
340 overflow: initial !important;
341 -webkit-print-color-adjust: exact;
342 }
343}
344
345@media print {
346 .page-break {
347 margin-top: 1rem;
348 display: block;
349 page-break-before: auto;
350 }
351}
352
353@page {
354 size: auto;
355 margin: 20mm;
356}
357```
358
359#### Troubleshooting Page Breaks
360
361If your content rendered as print media does not automatically break multi-page content into multiple pages, the issue may be
362
363- Style incompatibilities with print media rendering
364- A need to assign `CSS page-break-` properties to define how your document should behave when printed
365
366#### Common Page Break Pitfalls
367
368- A style of `overflow: scroll`, when rendered to print, will result in cut off content instead of page breaks to include the content
369- A style of `position: absolute`, when rendered to print, may result in reformatted, rotated, or re-scaled content, causing unintended affects to print page layout and page breaks
370- Using `flex` may interfere with page breaks, try using `display: block`
371
372### Handling Scrolling ([603](https://github.com/MatthewHerbst/react-to-print/issues/603))
373
374[![Edit react-to-print (Handling Scrolling)](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-to-print-handling-scrolling-n4mxyj?fontsize=14&hidenavigation=1&theme=dark)
375
376If you need to print the content of a scrolling container, you may encounter the following issues:
377
378- [Unable to control the scroll position](https://github.com/MatthewHerbst/react-to-print/issues/603#issue-1647664811), so the printed content may not be what you want.
379- [Overflow content is truncated](https://github.com/MatthewHerbst/react-to-print/issues/603#issuecomment-1649604330), resulting in missing printed content.
380
381To solve these problems, you need to modify the properties of the scrolling container when printing. You can pass a function to the `print` property, which will be called when printing. In this function, you can use the DOM API to query the scrolling container that needs to be modified, and then modify its properties to **control the scroll position**.
382
383```javascript
384const customToPrint = (printWindow) => {
385 const printContent = printWindow.contentDocument || printWindow.contentWindow?.document;
386 const printedScrollContainer = printContent.querySelector('.scroll-container');
387
388 const originScrollContainer = document.querySelector('.scroll-container');
389
390 // Set the scroll position of the printed container to match the origin container
391 printedScrollContainer.scrollTop = originScrollContainer.scrollTop;
392
393 // You can also set the `overflow` and `height` properties of the printed container to show all content.
394 // printedScrollContainer.style.overflow = "visible";
395 // printedScrollContainer.style.height = "fit-content";
396
397 printWindow.contentWindow.print();
398}
399
400const handlePrint = useReactToPrint({
401 // ...
402 print: customToPrint,
403});
404```
405
406#### Simple Show All Content
407
408In addition to the methods in the above example, you can also simply add a CSS class name to the scrolling container when printing to **show all content**.
409
410Set the container to `overflow: visible; height: fit-content` when printing, cancel the scrolling behavior when the content overflows, and make the height adapt to the content.
411
412```css
413@media print {
414 .scroll-container {
415 overflow: visible;
416 height: fit-content;
417 }
418}
419```
420
421> Note:
422>
423> - If the styles do not take effect, you can try using the `!important` modifier.
424> - The styles provided in the above instructions are for reference only. Complex situations may require more styles to achieve the desired result.
425
426## Running locally
427
428*NOTE*: The library is tested and built locally using Node >= 20.
429
430## Related
431
432* [vue-to-print](https://github.com/siaikin/vue-to-print): vue3 version of react-to-print