$assets.render draws a partial as an image, and returns the URL path of that image. Use it for any image that comes from your page data: a banner for each post, a badge for each tag, or a social card for each page.
{{#var file = 'banners/' + $utils.slug($page.title) + '.png'}}
{{#var banner = await $assets.render('banner', file, { width: 1600, height: 400 })}}
<img src="{{ banner }}" width="1600" height="400" alt=""/>For a social card on each page, see Open Graph images. It builds on this guide.
Install
bongo draws the image with Satori, and converts the drawing to pixels with resvg. They are optional peer dependencies, so add them to your site:
bun add -d satori @resvg/resvg-jsSVG output needs only satori. If a package is missing, the build fails and tells you what to install.
Arguments
{{#var url = await $assets.render(template, output, options)}}template— the name of a partial, such asog/cardforpartials/og/card.hbs.output— optional. The path of the image inpublic/, such asog/home.png. The extension sets the format. Withoutoutput, the image is a PNG with a name from its partial and its content, such ascard.3f9a1c2e.png.options.width,options.height— the size in pixels. The defaults are 1200 and 630, the size of a social card.options.args— values for the partial, as the attributes of{{#partial}}are.
The partial gets the $page, $pages, $section, and $tags of the page that calls $assets.render. This is also true when a layout or another partial calls it.
The return value is a URL path, such as /og/home.png. On a site under /blog/, it starts with /blog/. Use $utils.url.abs() to make a full URL.
Without output, the name changes when the image changes. Thus a host can cache these images for a long time, as it does other hashed files.
Formats
| Extension | Made with | Runtimes |
|---|---|---|
.svg |
Satori | Bun, Deno, and Node |
.png |
Satori and resvg | Bun, Deno, and Node |
.jpg, .jpeg, .webp |
Satori, resvg, and Bun.Image |
Bun only |
JPEG and WebP use Bun.Image, as image processing does. Thus they need the Bun runtime. Under Deno or Node, they fail the build. Their quality comes from $images.quality.
The template
A render template is a partial with one root element. Satori draws a subset of HTML and CSS, with flexbox layout. See the Satori documentation for the CSS that it supports.
<style>
@font-face { font-family: Inter; src: url(fonts/inter-700.woff); font-weight: 700 }
</style>
<div style="display: flex; flex-direction: column; justify-content: flex-end; width: 100%; height: 100%; padding: 64px; box-sizing: border-box; background: #ffd23f; font-family: Inter">
<img src="img/logo.svg" width="120" height="40">
<div style="margin-top: 16px; font-size: 80px">{{ $page.title }}</div>
<div style="margin-top: 12px; font-size: 28px">{{ $utils.excerpt($page.summary, 100) }}</div>
</div>- Set styles with the
styleattribute. Classes and stylesheets have no effect. - Give a box with padding
box-sizing: border-box, or Satori adds the padding to its width. - Put an element in place with
leftandtop. Satori does not always obeyrightandbottom. - An inline
<svg>needs awidthand aheight.
Fonts
Declare each font with an @font-face rule, in a <style> element of the template. The src is a path in assets/. bongo reads the rules, then removes the <style> element. A <style> element may hold only @font-face rules.
Satori reads TTF, OTF, and WOFF files, but not WOFF2. bongo uses the first src that is not WOFF2. A template with text must declare one font or more. The font files are not copied to public/.
Images
An <img src> or a CSS url() with a path reads that file from assets/. bongo puts the contents of the file into the drawing. A remote URL stays as it is: Satori gets it when it draws. The cache knows only its URL, so a changed remote image does not make a new drawing.
Dev and cache
bongo build draws all the images. bongo dev draws an image at its first request.
bongo keeps each drawing in node_modules/.cache/bongo/render. The key is a hash of the drawing, the fonts, the images, the size, the format, and the versions of Satori, resvg, and Bun. A rebuild copies unchanged images from the cache. $images.prune also cleans this folder. To clear the cache, delete the folder.
Errors
The build fails with a message that names the cause when:
- two pages draw different images to the same
output; outputis a file of the site, or a path outsidepublic/;- the template has no root element, or more than one;
- the template has text, but declares no fonts;
- a font is WOFF2, or a file is not in
assets/; - a
<style>element holds rules other than@font-face.