og is a small CLI for generating OpenGraph card images for blog posts and
static sites. It renders a fixed HTML/CSS template to PNG or JPG, with content,
colors, fonts, icons, and background images controlled by flags, frontmatter,
and og.toml.
The default card size is 1200x630.
Install og and render a themed card:
brew tap vrypan/tap
brew install og-imgog render \
--theme github-dark \
--title "OpenGraph cards from the terminal" \
--text "Theme presets, local assets, and reproducible rendering in a single CLI." \
--site "github.com/vrypan/og-img" \
--tags cli,rust,opengraph \
-o og.pngTo see all available themes, run:
og presetsCreate a project config when you want repeated renders to share the same site, theme, output directory, or build settings:
og initFor example:
[content]
site = "notes.example.com"
default_theme = "catppuccin-mocha"Then render with fewer flags:
og render \
--title "A themed card" \
--text "Project defaults keep repeated renders short." \
--tags notes,static-site \
-o og.pngUsing homebrew:
brew tap vrypan/tap
brew install og-imgTo build from this repository:
cargo build --releaseRun the binary directly:
./target/release/og --helpOr install it into Cargo's bin directory:
cargo install og-imgFrom a source checkout:
cargo install --path .Use a background image:
og render \
--title "Image-backed Cards" \
--text "Background images are embedded into the generated card." \
--bg-image test/bg.jpg \
--bg-image-overlay "rgba(0,0,0,0.45)" \
--fg "#ffffff" \
--muted "#e2e8f0" \
-o og.pngWrite bytes to stdout:
og render --title "Hello" -o - > og.pngog renderrenders one card from flags, a markdown file, or stdin.og buildrenders many markdown posts matched by a glob.og initwrites a starterog.toml.og presetslists configured themes from shared defaults, user config, and project config.og fontslists known system font families.
Theme presets live in themes/ and are installed with release
packages through defaults.toml.
Use --from to render one markdown file:
og render --from posts/example.md -o static/og/example.pngSupported frontmatter:
---
title: Building OpenGraph Cards Without a Browser
date: 2026-07-09
tags: [rust, opengraph, templates]
description: Repeatable social preview images for static sites.
og:
icon: assets/icon.png
bg_image: assets/card-bg.jpg
bg_image_overlay: rgba(0,0,0,0.45)
theme: dark
---description, summary, excerpt, or abstract becomes the card text. The markdown body
is not used unless you explicitly pass --text -.
og first loads optional shared defaults from defaults.toml, then optional
user defaults, then discovers og.toml by walking up from the current
directory, or you can pass --config path/to/og.toml.
Shared defaults are looked up with OG_DEFAULTS first, then
<prefix>/share/og-img/defaults.toml. The repository
defaults.toml contains the shared theme library.
User defaults are looked up with OG_CONFIG_HOME/og.toml first, then
$XDG_CONFIG_HOME/og/config.toml, $XDG_CONFIG_HOME/og/og.toml, and finally
~/.config/og/config.toml on Unix-like systems.
Example:
[output]
dir = "static/og"
format = "png"
size = "og"
scale = 2.0
naming = "{slug}.png"
skip_unchanged = true
[content]
site = "notes.example.com"
icon = "assets/avatar.png" # default icon for every card
date_format = "%-d %B %Y"
default_theme = "dark"
glob = "posts/**/*.md"
[fonts]
title = "Inter"
body = "Inter"
# title_file = "fonts/Inter-Bold.ttf"
# body_file = "fonts/Inter-Regular.ttf"
[themes.dark]
bg = "#101418"
fg = "#f2f4f8"
muted = "#8b95a5"
accent = "#5eead4"
tag_bg = "rgba(94,234,212,0.12)"
tag_fg = "#5eead4"
hairline = "rgba(255,255,255,0.08)"
bg_image_overlay = "rgba(0,0,0,0.35)"Configuration is merged in this order:
built-in defaults <- defaults.toml <- user config <- og.toml <- selected theme <- frontmatter <- CLI flags
Content:
--from <file.md>
--title <text>
--text <text>
--date <text>
--tags a,b,c
--site <name>
--icon <path>
--bg-image <path>
--monogram <char>
Theme and layout:
--bg <css-color>
--fg <css-color>
--muted <css-color>
--accent <css-color>
--tag-bg <css-color>
--tag-fg <css-color>
--bg-image-overlay <css-color>
--font-title <css-family>
--font-body <css-family>
--font-title-file <ttf>
--font-body-file <ttf>
--title-size <px>
--site-size <px>
--text-size <px>
--meta-size <px>
--radius <px>
--pad <px>
--theme <name>
Output:
--size og|twitter|square
--width <px>
--height <px>
--scale <number>
--format png|jpg
--check
-o, --output <path|->
Icons and background images must be local files; og never touches the
network, so builds stay reproducible. To shrink PNGs further, run the
standalone oxipng tool on the output.
Render all posts:
og build --glob 'posts/**/*.md'og build skips unchanged cards by default using a hash over the resolved card,
output settings, template, and embedded assets. Use --force to rerender
everything:
og build --forceThe card template lives in card.html and is embedded into the binary at
compile time. Editing card.html requires rebuilding og.
The template is intentionally fixed: users customize content and design tokens, not the document structure.
Generate sample card variations:
./test/generate_variations.shThe script writes example PNGs to test/.
MIT. See LICENSE.
