DEV Community

Cover image for Generate images from HTML in Node.js
🦁 Yvonnick FRIN
🦁 Yvonnick FRIN

Posted on • Originally published at yvonnickfrin.dev

Generate images from HTML in Node.js

As developers, we don't have great skills with image manipulation softwares, but we still need it. In my case I had to generate Twitter cards for social media and flyers for the meetup I co-organize. An important point for use is automation. So I wanted a solution that let me make a template and generates a lot of images without extra work.

That is why I created node-html-to-image. A Node.js module that generates images from HTML.

Here is the simplest example, you provide an output path and a HTML string. That's all!

const nodeHtmlToImage = require('node-html-to-image')

nodeHtmlToImage({
  output: './image.png',
  html: '<html><body>Hello world!</body></html>'
})
  .then(() => console.log('The image was created successfully!'))
Enter fullscreen mode Exit fullscreen mode

I talked about automation earlier. It comes with a templating engine, Handlebars. It enables creating multiple images using the same template.

In the following example, we changed world by a placeholder and inject its value with the content option.

const nodeHtmlToImage = require('node-html-to-image')

nodeHtmlToImage({
  output: './image.png',
  html: '<html><body>Hello {{name}}!</body></html>',
  content: { name: 'you' }
})
  .then(() => console.log('The image was created successfully!'))
Enter fullscreen mode Exit fullscreen mode

You want to generate images from HTML without writing a line of code? No problem, I also made a cli that use node-html-to-image underneath called node-html-to-image-cli.

npx node-html-to-image-cli index.html image.png
Enter fullscreen mode Exit fullscreen mode

Generated image:

Demonstration of node-html-to-image-cli

Feel free check out the GitHub repositories if you are interested:

Want to support ? Don't forget to leave a ⭐️


Feedback or ideas are appreciated 🙏 Please tweet me if you have questions @YvonnickFrin!

Top comments (24)

Collapse
 
helleworld_ profile image
Desiré 👩‍🎓👩‍🏫

¡Amazing!

Just tried it out, loving it! Just one question. If I use px to set the width of my elements, the image looks good:

But if I use % to set the width, then:

Why could this happen?

However, amazing project, thank you!

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Thank you Desiré!

Basically the module takes a screenshot of the body tag's content. So you need to fix the size of you image on this tag using CSS then you can use percent on body's children.

Maybe I should add this in the documentation?

Collapse
 
helleworld_ profile image
Desiré 👩‍🎓👩‍🏫 • Edited

Not sure, my code is as follows:

<body>
    <div class="card">
       [ ... ] (lots of subdivs)
    </div>
</body>

and my css:

        .card{
            width: 20%; /** if it's % then the image's width doesn't display properly **/
            box-shadow: 1px 3px 15px lightgray;
            height: 250px;
        }

I guess if it's a % then it won't work properly and you must set a fixed width for your elements before taking the screenshot!

I think it would be useful to add this as a sidenote, in case someone is setting all the components with %!

Also, it works perfectly fine with rem and em too.

EDIT: I'm using the CLI version just in case that matters!

Thread Thread
 
yvonnickfrin profile image
🦁 Yvonnick FRIN • Edited

Percentage are relative to your parent dimensions So it will be 20 percent of body. If you don't fix body size it will be relative to the browser's viewport I guess. I don't set any viewport. So I recommend fixing body size to the resolution you want for the output file.

In the example below I fix the size to A4 format in high resolution. The output file resolution will be 2480x3508. So the card size will be 20% of 2480px. You see what I mean?

<html>
  <head>
    <style>
       body {
         width: 2480px;
         height: 3508px;
       }
       .card{
         width: 20%; /** if it's % then the image's width doesn't display properly **/
         box-shadow: 1px 3px 15px lightgray;
         height: 250px;
        }
    </style>
  </head>
  <body>
    <div class="card">
       [ ... ] (lots of subdivs)
    </div>
  </body>
</html>
Thread Thread
 
helleworld_ profile image
Desiré 👩‍🎓👩‍🏫

Sure! I usually never set any size for the body, it should be nice to have it in mind so the image looks good, thank you!

Thread Thread
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Désiré I added a section about this in the documentation. What do you think of it?

Thread Thread
 
helleworld_ profile image
Desiré 👩‍🎓👩‍🏫

Clear as a sunny day, I think there can't be any risk now! Thank you!

Collapse
 
inatnat profile image
Nathaniel-kw Cheung

Super cool stuff

Is there any way to use custom font? I got a HTML like this but the output image is not using the font specified (it's good to show in browser)



<br>
body {<br>
width: 1000px;<br>
height: 500px;<br>
}</p>
<div class="highlight"><pre class="highlight plaintext"><code> @font-face {
font-family: JuneBug;
src: url('JUNEBUG.TTF');
}
h1 {
    font-family: JuneBug
}

&lt;/style&gt;
</code></pre></div>
<p></head></p>

<p><body>Hello world!<br>
<h1>Hey, June</h1><br>
</body></p>

<p></html></p>

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Hi Nathaniel,

TTF format is made for Safari, Android and iOS. The image is generated in a chromium browser. So you can only use WOFF format.

Collapse
 
wlun001 profile image
Wei Lun • Edited

This won't work on Docker, need to install anything other that npm install?

Collapse
 
quedicesebas profile image
Sebastián Rojas Ricaurte

You have to follow this for Docker github.com/puppeteer/puppeteer/blo... (tehere is specifict instructiosn for alpine also) and send this in puppeteerArgs when you call nodeHtmlToImage:

puppeteerArgs: {
headless: true,
args: [
"--no-sandbox",
"--remote-debugging-address=0.0.0.0",
"--remote-debugging-port=9222",
],
}

Collapse
 
quedicesebas profile image
Sebastián Rojas Ricaurte

¿Anyone?

Collapse
 
napoleon039 profile image
Nihar Raote

Looks so cool! It's interesting that I can use HTML and CSS to generate an image.

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Thank you Nihar! Sure, as a developper I'm used to HTML and CSS which are great tools to make nice graphical stuff 🙌

Collapse
 
dschep profile image
Daniel Schep

Is it possible to generate a GIF from animated HTML?

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

node-html-to-images don't handle this use case. It is an awesome idea!

Collapse
 
daniel13rady profile image
Daniel Brady

Nice work. 👍

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Thank you Daniel ❤️

Collapse
 
kaushan profile image
Kaushan

That’s really great ! I used to make it via screenshot inside headless browsers. This solution is much more elegant

Collapse
 
majiyd profile image
majiyd

Wow, Thank you for this great work!

I have a question though, Does it work with react Native? Thank you.

Collapse
 
peacefullatom profile image
Yuriy Markov

Awesomeness! 👍

Collapse
 
yvonnickfrin profile image
🦁 Yvonnick FRIN

Glad you like it ♥️

Collapse
 
tejesh002 profile image
tejesh002

This Package output only 800X600 dimension, but expecting dimension is greater then this.