Vijit Ail Software Engineer at toothsi. I work with React and NodeJS to build customer-centric products. Reach out to me on LinkedIn or Instagram.

Export React components as images using html2canvas

5 min read 1595

Export React Components Images html2canvas

As a developer, you’re probably always looking for new approaches or solutions to make the apps you build even more useful. For example, if you’re working on an app that generates graphical data, you may want to provide users with the ability to export some of the content to share or to refer to at a later date.

Many data visualization libraries allow users to export images. However, if you’re in search of a library that can export multiple tables and graphs from a page in one go, html2canvas may be your solution.

In this tutorial, we’ll demonstrate how to use html2canvas to export React components as images.

But first, let’s begin addressing some basic questions:

What is html2canvas?

html2canvas is a JavaScript library that enables users to produce screenshots of an entire webpage or portions of a webpage. The screenshots are taken directly on the user’s browser.

Technically, html2canvas does not actually take a screenshot, rather it creates a view based on the data that is present on the page. This results in an HTML5 canvas element that can be used to create an image file or to generate a preview of the screenshot on the user’s screen.

How does it work?

html2canvas reads a webpage as a canvas image. It goes through the webpage’s DOM and reads all present elements and styles. After html2canvas gathers all of the page structure information, it creates a representation of the page. Since all images are created on the user’s browser, html2canvas does not require any server-side rendering.

The resulting image is a very close facsimile of the webpage, but it’s not 100 percent perfect. That’s because it’s a representation of the webpage, rather than an actual screenshot.

How is it used?

To render an element with html2canvas, use the following syntax:

html2canvas(element[options]);

The html2canvas function accepts the DOM element and returns a P``romise containing the <canvas> element.

Next, we’ll use the then() promise fulfillment handler:

const captureElement = (element) => {
html2canvas(element).then(canvas => {
document.body.appendChild(canvas)
})
}

Alternatively, we can also use the async-await syntax to avoid the callback.

const captureElement = async (element) => {
const canvas = await html2canvas(element);
document.body.appendChild(canvas);
}

I personally prefer the async-await syntax, since it’s much cleaner than chaining .then() to the Promise.

What are the limitations?

html2canvas has a few limitations. For example, the library cannot render content plugins like Java Applets or Flash.



Also, html2canvas only renders properties that it can understand. There are many CSS properties, such as box-shadow or border-image, that do not currently work with this library. Here’s a full list of the supported and unsupported CSS properties.

Another limitation is that, without a proxy, any canvas elements with cross-origin content will not be readable by html2canvas. Cross-origin images are those that load from a third party or another domain. Examples could include iframes, stylesheets, or scripts.

Demo: exporting React components as images

Now that we have an in-depth understanding of what html2canvas is and how it works, it’s time to learn how to use this library to export React components as images.

Setting up the UI

First, we’ll create a <div> container in the JSX markup. We’ll use this to specify an area to download as an image. We’ll also add a button with an onClick event handler to download the image.

import React from "react";
import "./styles.css";

export default function App() {
return (
<>
<div className="parent">
<div>
<p>
Quis blandit turpis cursus in hac habitasse. Commodo quis imperdiet
massa tincidunt nunc pulvinar sapien et ligula. Sit amet dictum sit
amet justo donec. Cursus mattis molestie a iaculis. Vel pretium
lectus quam id leo in vitae. Quam nulla porttitor massa id neque
aliquam vestibulum morbi blandit.
</p>
</div>
</div>
<button>
Capture Image
</button>
</>
);
}

Implementing the logic

Once we’ve set up the user interface, the next step is to add code that will allow us to download the React components as images. To do this, we’ll follow three steps:

  1. Install the html2canvas npm package
npm i html2canvas
  1. Import and use the html2canvas function

To do this, we’ll create a utils directory inside the src folder and then create a new file, exportAsImage.js:

import html2canvas from "html2canvas";

const exportAsImage = async (el, imageFileName) => {
const canvas = await html2canvas(element);
const image = canvas.toDataURL("image/png", 1.0);
// download the image
};

In the above code snippet, html2canvas takes a DOM element in the argument and then uses the element to create a canvas image. Then, it returns a promise containing a canvas element.

  1. Download the exported image
import html2canvas from "html2canvas";const exportAsImage = async (el, imageFileName) => {
const canvas = await html2canvas(element);
const image = canvas.toDataURL("image/png", 1.0);
downloadImage(image, imageFileName);
};const downloadImage = (blob, fileName) => {
const fakeLink = window.document.createElement("a");
fakeLink.style = "display:none;";
fakeLink.download = fileName;

fakeLink.href = blob;

document.body.appendChild(fakeLink);
fakeLink.click();
document.body.removeChild(fakeLink);

fakeLink.remove();
};

export default exportAsImage;

In the downloadImage function, we create an anchor link in memory to simulate a click and download the image. Once the click event is simulated, the fake anchor link, fakeLink, is removed from the DOM.

Next, in the App.js file, we import the exportAsImage function and attach it to the button’s onClick handler. We use the useRef``() hook to create a reference to the DOM element and pass it to the exportAsImage function along with the filename.

export default function App() {
const exportRef = useRef();

return (
<>
<div className="parent">
<div ref={exportRef}>
<p>...</p>
</div>
</div>
<button onClick={() => exportAsImage(exportRef.current, "test")}>
Capture Image
</button>
</>
)
}

That’s the process for downloading a React component as an image!

Handling horizontal overflow

html2canvas works nearly perfectly when exporting images that have a vertical layout. However, it has some limitations with horizontal layout. This library can only capture visible elements. Any elements that are outside of the viewport will be excluded from the final screenshot. Similarly, any portions of elements that are not displayed on the screen, will be excluded from the final screenshot as well.


More great articles from LogRocket:


Unfortunately, a vertical layout is not suitable for many React apps. An example is a use case in which many tables, visuals, or graphs need to be included on a single page. Comparing graphs or tables side by side is more difficult in a vertical layout. The images generally end up being very tall, and this does not result in a good user experience.

A workaround for this issue is to set the html and body tags to a large value so they can easily accommodate all the available data so that no data is hidden by the container.

Next, we capture the data and create a snapshot. Then, we set the html and body tags back to their original values.

Here’s the updated function for this method:

const exportAsImage = async (element, imageFileName) => {
const html = document.getElementsByTagName("html")[0];
const body = document.getElementsByTagName("body")[0];
let htmlWidth = html.clientWidth;
let bodyWidth = body.clientWidth;
const newWidth = element.scrollWidth - element.clientWidth;
if (newWidth > element.clientWidth) {
htmlWidth += newWidth;
bodyWidth += newWidth;
}
html.style.width = htmlWidth + "px";
body.style.width = bodyWidth + "px";
const canvas = await html2canvas(element);
const image = canvas.toDataURL("image/png", 1.0);
downloadImage(image, imageFileName);
html.style.width = null;
body.style.width = null;
};

In this function, we first retrieve the initial width of html and body tags. Then, we note the inner width of an element and the minimum width required to contain all the data in the viewport without adding a horizontal scrollbar.

We define the following:

  • clientWidth: the inner width of an element in pixels, including padding
  • scrollWidth: the minimum width that an element needs to fit in the container

Wondering why we need these values? Well, by keeping the size dynamic, rather than static, we can accommodate a maximum number of use cases. For example. if there are only two visuals on the screen, we will not need to set the container width very high. But, if there are several visuals on the screen, we may need a high container width.

For each case, we compare the clientWidth and scrollWidth to know if a change in width is required. There are two possible scenarios:

  1. clientWidth is less than scrollWidth

In this scenario, portions of the container are hidden. To overcome this problem, we’ll add the difference between scrollWidth and clientWidth to the width of html and body tags.

  1. clientWidth is greater than or equal to scrollWidth

In this scenario, all parts of the container are visible. In this case, there is no need to adjust the width of html and body tags.

Conclusion

html2canvas is a robust, easy to use solution for exporting React components as images. It does have some limitations, so be sure to read the documentation and test all possible scenarios to ensure you’re able to generate the desired view. The demo used in this tutorial is available on CodeSandbox.

Get setup with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Vijit Ail Software Engineer at toothsi. I work with React and NodeJS to build customer-centric products. Reach out to me on LinkedIn or Instagram.

Leave a Reply