Printing in React.js
In this article we shall discuss the following topics and make it as clear as we can:
Prerequisites
To get the most out of this article you are expected to know at least the basics of the following technologies:
Am using Vite but you can use Create React App, Gatsby, or whatever tool you are comfortable using React.js with. Below is my project structure:
├── node_modules/
├── public/
├── src/
├── assets/
├── components/
├── Portrait.jsx
├── Landscape.jsx
├── App.css
├── index.css
├── index.jsx
├── index.html
├── .gitignore
├── package.json
├── README.md
Introduction to printing in React.js
If you have been building single-page applications ( SPAs) with React.js you will most likely like want to print something at some point. Be it an invoice, transaction history, certificates, etc.
The npm package we will be using in this article is react-to-print. Hence you can go ahead and run npm i react-to-print in your project.
Designing your print template
Designing your print template is nothing out of this world. It is just a normal React Component with your styling. I will be using tailwind css for my design but you can do whatever works for you.
Congratulations on making it this far and thank you for your patience. It has been a lot. Let's hop into the implementation.
Recommended by LinkedIn
How to print in portrait and Landscape (Implementation)
app.jsx ( Scroll below for the copyable code. Am using screenshots because the LinkedIn code/syntax highlighter is just that...)
import React, { useRef } from "react";
/**
* First we import the useReactToPrint hook from react-to-print
* This hook is how we configure our print Document/Template
*/
import { useReactToPrint } from "react-to-print";
import Portrait from "./components/Portrait";
import Landscape from "./components/Landscape";
const App = () => {
/**
* We are using refs to target our prints/templates
*
* What we currently have is two refs for portrait and landscape printing
* We are doing this because what we want is when the user click on either portrait or landscape to make their wish come through
*
*/
const portraitComponentRef = useRef();
const landscapeComponentRef = useRef();
/**
* The useReactToPrint hook comes equipped with alot of properities you can pass to it.
* However, we are just going to explore 2 for this article.
* content - Is the ref value to the component we are printing. In this case Portrait.jsx and landscape.jsx
* documentTitle - Is the default title of the document incase you decide to save the print instead of printing.
*/
const handlePrintForPortrait = useReactToPrint({
content: () => portraitComponentRef.current,
documentTitle: "portrait-print",
});
const handlePrintForLandscape = useReactToPrint({
content: () => landscapeComponentRef.current,
documentTitle: "landscape-print",
});
return (
/**
* For our styling we are using tailwind css. Is just two cards with the cursor-pointer and a full-height screen and a gray background with some text labels.
*/
<div className="flex flex-col gap-3 items-center justify-center h-screen">
<h3 className="text-3xl mb-3">Choose your printing?</h3>
<div className="bg-gray-50 flex items-stretch gap-3">
<div
/**
* First let me help you with the differnce between onClick={() => handlePrintForPortrait()} and onClick={handlePrintForPortrait}
* I asked ChatGPT for help and this is what it has to say:
* onClick={handlePrintForPortrait} directly passes a reference to the handlePrintForPortrait function, which is invoked only when the click event occurs.
* onClick={() => handlePrintForPortrait()} wraps the handlePrintForPortrait function in an arrow function, immediately executing it. This approach is useful when you need to pass arguments or manipulate the context (this).
* The first approach is concise and often more performant, while the second provides more control over function invocation.
*
* This also applied to handlePrintForLandscape
*/
onClick={handlePrintForPortrait}
className="bg-white shadow-xl rounded-2xl w-96 h-96 cursor-pointer hover:shadow-2xl flex items-center justify-center"
>
<h3 className="text-2xl font-bold">Portrait</h3>
</div>
<div
onClick={handlePrintForLandscape}
className="bg-white shadow-xl rounded-2xl w-96 h-96 cursor-pointer hover:shadow-2xl flex items-center justify-center"
>
<h3 className="text-2xl font-bold">Landscape</h3>
</div>
</div>
{/* Our Component refs */}
{/**
* We are wrapping our prints/component refs with a div of className hidden
* Tailwind 'hidden' class is the same as display: none; in CSS
*
* Why are we doing this?
* If we do not do this the component refs or prints will appear in our DOM and we definitely do not want that.
*
*/}
<div className="hidden">
{/* Portraits print component starts here */}
<div ref={portraitComponentRef}>
<Portrait />
{/* This part is optional but it is required if you want to change the orientation of your page */}
<style type="text/css" media="print">
{
"\
@page { size: portrait; }\
"
}
</style>
</div>
{/* Portraits print component ends here */}
{/*
////////////////////////////////////////////////////////////////////////////////////
*/}
{/* Landscape print component starts here */}
<div ref={landscapeComponentRef}>
<Landscape />
{/* This part is optional but it is required if you want to change the orientation of your page */}
<style type="text/css" media="print">
{
"\
@page { size: landscape; }\
"
}
</style>
</div>
{/* Landscape print component ends here */}
</div>
</div>
);
};
export default App;
Code Explanation
The code is well-commented and I like to think it is self-explanatory. For your convenience check it out on GitHub or you can comment on the article for any clarifications needed.
Gotchas, tips, and advises
<div style={{ display: "none" }}>
<div ref={componentRef}>
<ComponentToPrint />
</div>
</div>
<style type="text/css" media="print">{"\
@page {\ size: landscape;\ }\
"}</style>
<div className="parent">
<p>Hello</p>
</div>
div.parent p { color:red; }
Links (Documentations and GitHub repo)