React: Download HTML Element as an image file
Tags: javascript, react, tutorial, beginners
Published: 2022-03-01 12:52:37

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](https://github.com/hsk-kr/landing-page) and [DEMO](https://hsk-kr.github.io/landing-page/))

First of all, download the packages.

```
npm i downloadjs html2canvas
```

or

```
yarn add downloadjs html2canvas
```

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

```
npm i -D @types/downloadjs
```

or

```
yarn add -D @types/downloadjs
```

**html2canvas**: Drawing HTML Element in Canvas
**downloadjs**: Download Library

## Whole HTML

![HTML](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xr4tleticu5d3k7lw95k.png)

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

```javascript
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>
```

That's it. 

![Captured Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d2yus3dpdryqpbtmjkf6.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mnk0w0hyb7s6fxhf98xy.png)

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

[Pricing Table]
```javascript
// ...

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>
  );
};

```

[Navbar]
```javascript

// ...
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');
  };

// ...
```

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

![Downloaded Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbjcphkngxmc1cm0wnf5.png)
 
It worked well.

## Specific Element (Scrolling Capture)

How about capturing an element that has a scroll?

![Pricing Table has a scroll](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fxgjyjn1dbj8c6rai1rj.png)
 
I changed `height` of Pricing Table to `200px`.
The scroll appears. 

How does it work with the previous code?

![Cropped Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2j5i9v3d0kqc7em5zfm.png)
 
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.

```javascript
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');
  };
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/301kjyrvpifumyyhnwe0.png)

Here, you see the final result.

---

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