Generating images from html in node with satori + resvg


Dynamically generated thumbnails!

Thats right, its a blog update.

I now generate og/meta images using satori + Resvg based on a html template, in node!

Setup


(this is using ESM, not commonjs - I recently ported my project to ESM as part of this so thought i should mention)

install the modules

  npm i satori
  npm i satori-html
  npm i @resvg/resvg-js

we create our main file and import our modules

  import satori from "satori";
  import { html } from "satori-html";
  import { Resvg } from "@resvg/resvg-js";

  import {fileURLToPath} from 'url';
  import path from 'path';

basically, I take a template from a html template existing in the filesystem, and replace certain tags.

make template.html in the root of your project (or wherever you want it- really)

  <!DOCTYPE html>
  <html lang="en">
  <head>
  <meta charset="UTF-8">
  <style>
  * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
  }
  body {
      width: 1200px;
      height: 628px;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: black;
      background-image: radial-gradient(circle, #ffffff 1px, black 1px);
      background-size: 40px 40px;
      color: white;
      font-family: sans-serif;
      margin: 0 auto;
  }
  .container {
      display: flex;
      width: 100%;
      height: 100%;
      padding: 50px;
  }
  .container img {
      width: 500px;
      height: 500px;
      flex-shrink: 0;
  }
  .text {
      display: flex;
      flex-direction: column;
      padding-left: 50px;
      flex-grow: 1;
      justify-content: center;
  }
  .text h1 {
      font-size: 48px;
      margin-bottom: 20px;
  }
  .text p {
      font-size: 24px;
  }
  </style>
  </head>
  <body>
  <div class="container">
      <img src="https://cataas.com/cat?type=square" alt="Random Cat">
      <div class="text">
          <h1>{{title}}</h1>
          <hr>
          <p>{{headline}}</p>
      </div>
  </div>
  </body>
  </html>

Fonts: you need a font to render text. I used Victor Mono, but as long as its a .ttf or .otf it should work

and finally, some code in our main file.

  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);

  async function generateImage(title, headline){
    const templatePath = path.join(__dirname, 'template.html');

    let template = await fs.readFile(templatePath, 'utf8');

    template = template
      .replaceAll('{{title}}', title)
      .replaceAll('{{headline}}', headline)

    const markup = html(template);

    const fontData = await fs.readFile(
      "VictorMono-Regular.ttf"
    );

    const svg = await satori(markup, {
      width: 1200,
      height: 628,
      fonts: [
        {
          name: "Arial",
          data: fontData,
          weight: "auto",
          style: "normal",
        },
      ],

    });


    const resvg = new Resvg(svg, {
      background: "rgba(255, 255, 255, 1)",
    });

    const pngData = resvg.render();
    const pngBuffer = pngData.asPng();
    fs.writeFile("./generated/" + title + ".png", pngBuffer)
  }

finally, we can generate images with a simple function call

  generateImage('awesome', 'sauce');

Conclusion


Satori + resvg is a powerful combination, with the code I provided, you should be able to make any type of image you want from html!

I really need to make a comment section, but if you need anything / just want to tell me something, feel free to email me

vesania at exonauto.me

Thanks for reading, have a wonderful day :)