DEV Community

loading...
Cover image for Generate images from HTML in Node.js

Generate images from HTML in Node.js

🦁 Yvonnick FRIN
Developer Web Fullstack at @Pix. I'm a @NantesJS meetup co-organizer on my spare time.
Originally published at yvonnickfrin.dev ・2 min read

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!

Discussion (23)

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 Author

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 Author • 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 Author

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
wlun001 profile image
Wei Lun • Edited

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

Collapse
sebrojas14 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
sebrojas14 profile image
Sebastián Rojas Ricaurte

¿Anyone?

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 Author

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
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 Author

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 Author

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 Author

Thank you Daniel ❤️

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 Author

Glad you like it ♥️

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