DEV Community

Cover image for React: Download HTML Element as an image file
SeongKuk Han
SeongKuk Han

Posted on • Edited on

React: Download HTML Element as an image file

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!

Top comments (6)

Collapse
 
adirioghenetega profile image
Adiri Oghenetega (@Thatcoderguy)

hello , thanks for this . I tried it right away and noticed that the downloaded file excludes image tags within div . that means background images and other photos within the area selected appeared blank on the downloaded file . please explain what i'm doing wrong, thanks

Collapse
 
lico profile image
SeongKuk Han

You tried first one? donload body as an image?? I need to know more about situation to know what that was wrong

Collapse
 
adirioghenetega profile image
Adiri Oghenetega (@Thatcoderguy) • Edited

i tried capturing a particular div within the body . the div contains an image tag with an external source(fetched through the pexels API)

Thread Thread
 
lico profile image
SeongKuk Han

An external source. Okay, I'll make an exmaple and try then I'll tell you. If I don't make the same situation, It would be good that you give me the source code(not your project code) that has same problems.

Thread Thread
 
adirioghenetega profile image
Adiri Oghenetega (@Thatcoderguy)

okay , great

Thread Thread
 
lico profile image
SeongKuk Han • Edited

I'm afraid to say that. html2canvas seems not working with external images.
I found a tric for this. But, it didn't seem working in all cases.
You put the images into a canvas manually, then the images will be included in the download image. However, you should consider yourself like image sizes, styles, which images apprear..

I coded the example and uploaded to Github. I hope you will find something useful in this code.

github.com/hsk-kr/html-2-canvas-ex...

Have a happy coding!