react-to-print

repository·master·Indexed 25 days ago

https://github.com/matthewherbst/react-to-print

A library for printing React components in the browser by rendering them into a hidden iframe. Version 3.3.0 provides the useReactToPrint hook to trigger print dialogs, with support for custom document titles, before/after print callbacks, custom fonts, and PDF generation via a custom print callback.

Tokens
6.2K
Snippets
15
Records
24
Agent score
32%

What's inside react-to-print

  1. Handle asynchronous state updates in `onBeforePrint`

    master

    Because React state updates are asynchronous, you must return a Promise from onBeforePrint and resolve it only after the state update has completed. This ensures react-to-print waits for the DOM to reflect the new state before printing.

    const [isPrinting, setIsPrinting] = useState(false);
    const contentRef = useRef(null);
    const promiseResolveRef = useRef(null);
    
    useEffect(() => {
      if (isPrinting && promiseResolveRef.current) {
        promiseResolveRef.current();
      }
    }, [isPrinting]);
    
    const handlePrint = useReactToPrint({
      contentRef,
      onBeforePrint: () => {
        return new Promise((resolve) => {
          promiseResolveRef.current = resolve;
          setIsPrinting(true);
        });
      },
      onAfterPrint: () => {
        promiseResolveRef.current = null;
        setIsPrinting(false);
      }
    });

    Note for Class components: Pass the Promise resolve function to the this.setState callback: this.setState({ isPrinting: false }, resolve)

    const [isPrinting, setIsPrinting] = useState(false);
    const contentRef = useRef(null);
    
    // We store the resolve Promise being used in `onBeforePrint` here
    const promiseResolveRef = useRef(null);
    
    // We watch for the state to change here, and for the Promise resolve to be available
    useEffect(() => {
      if (isPrinting && promiseResolveRef.current) {
        // Resolves the Promise, letting `react-to-print` know that the DOM updates are completed
        promiseResolveRef.current();
      }
    }, [isPrinting]);
    
    const handlePrint = useReactToPrint({
      contentRef,
      onBeforePrint: () => {
        return new Promise((resolve) => {
          promiseResolveRef.current = resolve;
          setIsPrinting(true);
        });
      },
      onAfterPrint: () => {
        // Reset the Promise resolve so we can print again
        promiseResolveRef.current = null;
        setIsPrinting(false);
      }
    });
  2. Handle scrolling containers during print

    master

    Printing a container with overflow: scroll often results in truncated content or incorrect scroll positions. You can solve this using the print callback in useReactToPrint to manipulate the DOM of the print window directly.

    Option 1: Using the print callback to sync scroll position

    const customToPrint = (printWindow) => {
      const printContent = printWindow.contentDocument || printWindow.contentWindow?.document;
      const printedScrollContainer = printContent.querySelector('.scroll-container');
      const originScrollContainer = document.querySelector('.scroll-container');
    
      // Set the scroll position of the printed container to match the origin container
      printedScrollContainer.scrollTop = originScrollContainer.scrollTop;
    
      printWindow.contentWindow.print();
    }
    
    const handlePrint = useReactToPrint({
      // ...
      print: customToPrint,
    });

    Option 2: Simple CSS approach (Show all content) Apply these styles to the scrolling container to force it to expand to its full height during print:

    @media print {
      .scroll-container {
        overflow: visible;
        height: fit-content;
      }
    }
    const customToPrint = (printWindow) => {
      const printContent = printWindow.contentDocument || printWindow.contentWindow?.document;
      const printedScrollContainer = printContent.querySelector('.scroll-container');
    
      const originScrollContainer = document.querySelector('.scroll-container');
    
      // Set the scroll position of the printed container to match the origin container
      printedScrollContainer.scrollTop = originScrollContainer.scrollTop;
    
      // You can also set the `overflow` and `height` properties of the printed container to show all content.
      // printedScrollContainer.style.overflow = "visible";
      // printedScrollContainer.style.height = "fit-content";
    
      printWindow.contentWindow.print();
    }
    
    const handlePrint = useReactToPrint({
      // ...
      print: customToPrint,
    });
  3. Set custom page margins

    master

    To set custom margins for the printed page, create a function that returns a CSS string for the @page rule and include it within a <style> tag inside the component being printed.

    const getPageMargins = () => {
      return `@page { margin: ${marginTop} ${marginRight} ${marginBottom} ${marginLeft} !important; }`;
    };

    In your JSX:

    <style>{getPageMargins()}</style>
  4. Set page orientation and size via CSS

    master

    To force a specific page orientation (like landscape) or a custom page size, include a <style> tag directly inside the component being passed as the content ref.

    Landscape Orientation:

    <style type="text/css" media="print">{"\n  @page { size: landscape;\}\n"}</style>

    Custom Page Size: Most browsers support the CSS @page size property. Use it within a @media print block:

    @media print {
      @page {
        size: 50mm 150mm;
      }
    }
  5. Force specific themes (Dark/Light mode) for print

    master

    Because the print iframe is a fresh document, it may default to the user's system prefers-color-scheme rather than your application's current theme. To force a specific theme (e.g., always printing in light mode), explicitly define your colors within a @media print block:

    @media print {
      .my-content {
        background: #fff;
        color: #000;
      }
    }
  6. Hide or show content during printing using CSS

    master

    The recommended way to control visibility during printing is via CSS Media Queries. You can hide an element by default and only show it when the media type is print.

    .printContent {
      display: none;
    
      @media print {
        display: block;
      }
    }

    Usage example:

    const contentRef = useRef<HTMLDivElement>(null);
    const reactToPrintFn = useReactToPrint({ contentRef });
    
    return (
      <div>
        <button onClick={reactToPrintFn}>Print</button>
        <div className="printContent" ref={contentRef}>Content to print</div>
      </div>
    );
  7. Print Class components by forwarding refs

    master

    To print a Class component, you must manually forward the contentRef as a prop to an internal DOM element within that component.

    class ComponentToPrint extends Component {
      render() {
        return (
          <div ref={this.props.innerRef}>
            Print content
          </div>
        )
      }
    }
    
    function App() {
      const contentRef = useRef(null);
      const handlePrint = useReactToPrint({ contentRef });
    
      return (
        <div>
          <button onClick={handlePrint}>Print</button>
          <ComponentToPrint innerRef={contentRef} />
        </div>
      );
    }
  8. Implement page breaks for dynamic content

    master

    To control where pages break in dynamic lists, use a dedicated page-break element and CSS.

    1. JSX Pattern:

    <div className="print-container" style={{ margin: "0", padding: "0" }}>
      {listOfContent.map(yourContent => (
        <>
          <div className="page-break" />
          <div>{yourContent}</div>
        </>
      ))}
    </div>

    2. CSS Pattern:

    @media all {
      .page-break {
        display: none;
      }
    }
    
    @media print {
      html, body {
        height: initial !important;
        overflow: initial !important;
        -webkit-print-color-adjust: exact;
      }
    }
    
    @media print {
      .page-break {
        margin-top: 1rem;
        display: block;
        page-break-before: auto;
      }
    }
    
    @page {
      size: auto;
      margin: 20mm;
    }

    Common Pitfalls:

    • overflow: scroll will cause content to be cut off instead of breaking pages.
    • position: absolute can cause reformatting or scaling issues.
    • display: flex can interfere with page breaks; use display: block for print layouts.
  9. Hide a component from the UI but keep it in the DOM for printing

    master

    If you have a component intended only for printing that should not be visible in the main application UI, wrap it in a div with display: none. This keeps the component in the DOM so react-to-print can access it, but hides it from the user.

    <div style={{ display: "none" }}><ComponentToPrint ref={componentRef} /></div>
  10. Configure Tailwind CSS for printing

    master

    Tailwind CSS is compatible with react-to-print, but ensure the following:

    1. Global Styles: Your Tailwind stylesheet must be loaded in the main document's <head>. Do not set ignoreGlobalStyles: true in your react-to-print configuration.
    2. Print Utilities: Use the Tailwind print: variant for styles that should only apply during printing (e.g., class="hidden print:block").
    3. Targeting: Ensure styles target the printed nodes directly. Styles applied to unprinted parent elements may not propagate to the print iframe.
  11. What types are accepted by the useReactToPrint hook content argument

    master
    The useReactToPrint hook accepts a UseReactToPrintHookContent value, which allows you to either pass a standard React UI event directly or a function that returns a ContentNode. Passing a function allows the hook to resolve the content dynamically at the moment the print action is triggered, which is useful for handling events or generating content on-demand.