Skip to content

Instantly share code, notes, and snippets.

@mjsarfatti
Created January 16, 2021 22:36
Show Gist options
  • Save mjsarfatti/daca9525a7f3d2276db2dbd0e565d619 to your computer and use it in GitHub Desktop.
Save mjsarfatti/daca9525a7f3d2276db2dbd0e565d619 to your computer and use it in GitHub Desktop.
OG Image generator for Gatsby

0. Install packages

npm install --save-dev @babel/register @babel/plugin-proposal-class-properties

  • or -

yarn add --dev @babel/register @babel/plugin-proposal-class-properties

0.bis Also install

npm install --save-dev puppeteer fs-extra gatsby-source-filesystem

  • or -

yarn add --dev puppeteer fs-extra gatsby-source-filesystem

1. In package.json

Add Babel configuration so you can use React and JSX to build your cards:

...
  "babel": {
    "presets": [
      "@babel/env",
      "@babel/react"
    ],
    "plugins": [
        [
          "@babel/plugin-proposal-class-properties"
        ]
    ]
  }
...

2. In gatsby-node.js

At the top of the file add

require("@babel/register")

Then basically follow https://andrewingram.net/posts/automatic-social-cards-with-gatsby/ > Integrating with Gatsby

/* Mostly taken from
https://andrewingram.net/posts/automatic-social-cards-with-gatsby/ */
const { writeFile } = require("fs")
const { resolve } = require("path")
const { createHash } = require("crypto")
const { promisify } = require("util")
const React = require("react")
const ReactDOMServer = require("react-dom/server")
const writeFileAsync = promisify(writeFile)
/**
* Writes a file to the cache location
*/
async function writeCachedFile(CACHE_DIR, key, contents, extension) {
// I'm using the title as the key for the hash, because it's the only
// thing which impacts the final image. If you were to have something
// more elaborate, you should just use the HTML as the hash instead.
const fileName = createHash("md5").update(key).digest("hex") + "." + extension
const absolutePath = resolve(CACHE_DIR, fileName)
await writeFileAsync(absolutePath, contents)
return absolutePath
}
/*
* Returns the path to an image generated from the provided HTML.
*/
async function imageFromHtml(CACHE_DIR, browser, title, html) {
// Write the HTML to a file and get its filename
const filePath = await writeCachedFile(CACHE_DIR, title, html, "html")
const page = await browser.newPage()
// Navigate to our saved HTML
await page.goto(`file://${filePath}`)
// My HTML includes webfonts, so make sure they're ready
await page.evaluateHandle("document.fonts.ready")
// Set the viewport to the desired dimensions of the image
await page.setViewport({ width: 1200, height: 630 })
// Take a screenshot, we use PNG because it's higher quality - and the
// compression works well for images which contain a lot of areas of
// solid colour.
const file = await page.screenshot({ type: "png" })
// Write the screenshot to a file, and return its filename
return writeCachedFile(CACHE_DIR, title, file, "png")
}
exports.default = async function postToImage(
CACHE_DIR,
browser,
title,
componentPath,
props
) {
const UserComponent = require(componentPath).default
const element = React.createElement(UserComponent, props)
const html = ReactDOMServer.renderToStaticMarkup(element)
return imageFromHtml(CACHE_DIR, browser, title, html)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment