React: Download HTML Element as an image file

SeongKuk Han - Mar 1 '22 - - Dev Community

Recently, I got a job to download html as an image. Even though it was easy to find related libraries, it was kind of tricky to take a full capture of an element that has a scroll.

I will use my landing page in this post. (GITHUB and DEMO)

First of all, download the packages.

npm i downloadjs html2canvas
Enter fullscreen mode Exit fullscreen mode

or

yarn add downloadjs html2canvas
Enter fullscreen mode Exit fullscreen mode

And if you're using typescript, you must download type package.

npm i -D @types/downloadjs
Enter fullscreen mode Exit fullscreen mode

or

yarn add -D @types/downloadjs
Enter fullscreen mode Exit fullscreen mode

html2canvas: Drawing HTML Element in Canvas
downloadjs: Download Library

Whole HTML

HTML

I added a list item in Navbar.
I will implement the download feature on the click event of the list item.

import downloadjs from 'downloadjs';
import html2canvas from 'html2canvas';

// ...

const handleCaptureClick = async () => {
    const canvas = await html2canvas(document.body);
    const dataURL = canvas.toDataURL('image/png');
    downloadjs(dataURL, 'download.png', 'image/png');
  };

// ...

<li>
  <a href="#" onClick={handleCaptureClick}>
    Capture
  </a>
</li>
Enter fullscreen mode Exit fullscreen mode

That's it.

Captured Image

Now, click the capture text and you will get that image.
It's very simple.

Specific Element

What if you want to capture a specific element?
There is nothing too difficult, just change the target document.body to an element you want.

Pricing Table

Let's say, we're going to capture Pricing Table section.

[Pricing Table]

// ...

const Prices: React.FC = () => {
  // I added the class name 'pricing-table' to get the table element easily.
  return (
    <div className={cx('prices-section') + ' pricing-table'}>
      <SectionTitle>Pricing Table</SectionTitle>
      <div className={cx('plans')}>
        {plans.map((plan, planIdx) => (
          <PlanCard
            key={planIdx}
            href={plan.href}
            title={plan.title}
            price={plan.price}
            perDate={plan.perDate}
            features={plan.features}
          />
        ))}
      </div>
    </div>
  );
};

Enter fullscreen mode Exit fullscreen mode

[Navbar]


// ...
const handleCaptureClick = async () => {
    const pricingTableElmt =
      document.querySelector<HTMLElement>('.pricing-table');
    if (!pricingTableElmt) return;

    const canvas = await html2canvas(pricingTableElmt);
    const dataURL = canvas.toDataURL('image/png');
    downloadjs(dataURL, 'download.png', 'image/png');
  };

// ...
Enter fullscreen mode Exit fullscreen mode

I just replaced document.body to document.querySelector('pricing-table).

Downloaded Image

It worked well.

Specific Element (Scrolling Capture)

How about capturing an element that has a scroll?

Pricing Table has a scroll

I changed height of Pricing Table to 200px.
The scroll appears.

How does it work with the previous code?

Cropped Image

The image is cropped.
But this is how it actually works because that's what we can see at this moment.

For capturing a whole element without a scroll, I use some tricks.

const handleCaptureClick = async () => {
    const pricingTableElmt =
      document.querySelector<HTMLElement>('.pricing-table');
    if (!pricingTableElmt) return;

    const copiedPricingTableElmt = pricingTableElmt.cloneNode(
      true
    ) as HTMLElement;
    copiedPricingTableElmt.style.position = 'fixed';
    copiedPricingTableElmt.style.right = '100%';
    copiedPricingTableElmt.style.height = 'auto';

    document.body.append(copiedPricingTableElmt);

    const canvas = await html2canvas(copiedPricingTableElmt);

    copiedPricingTableElmt.remove();

    const dataURL = canvas.toDataURL('image/png');
    downloadjs(dataURL, 'download.png', 'image/png');
  };
Enter fullscreen mode Exit fullscreen mode

I cloned the element then set position to fixed and right to 100%.

Now, you won't be able to see it. Even if it is appended to body.

Then I adjusted the size. For this case, it's enough to change height to auto.

Don't forget to remove the copied element after calling html2canvas.

Captured Image

Here, you see the final result.


I hope this will be helpful for someone.
Happy Coding!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player