metaplate
v0.8.4
Published
Typed social-image contracts for TypeScript: rendering, metadata, routes, and verification.
Maintainers
Readme
Metaplate
Composable, framework-neutral Open Graph image tooling for TypeScript.
Define your social images once. Render, serve, and publish them consistently across your TypeScript app.
Metaplate turns a single branded JSX plate into the complete Open Graph image pipeline. Use the same definition to render SVG or raster images in several sizes, serve them from Node-compatible or edge routes, generate matching Open Graph and X metadata, emit the <meta> tags, manage fonts, produce predictable image URLs, and verify the files you ship, locally or at a deployed URL.
The same metadata also produces Open Graph video and audio tags, X player and app cards, Schema.org ImageObject JSON-LD, and Adaptive Cards for Teams, Slack, and Discord previews.
It works with plain Node, static build scripts, Next.js, Astro, React Router, SvelteKit, Express, Bun, and Deno, and on Deno-based edges such as Netlify Edge Functions and Deno Deploy, without tying your image design to a single framework.
Contents
- Install
- Quick start
- Where to go next
- Guides
- Rendering and routes
- Static files with
metaplate render - Live preview with
metaplate preview - Signed image routes
- Framework and runtime support
- Edge runtimes
- Next.js
- Square and landscape variants
- Metadata and meta tags
- X player and app cards
- Social compatibility profiles
- Teams, Slack, and Discord previews
- Schema.org image metadata
- Fonts
- Emoji
- Verifying images
- Deployment routes
- Entry points
- License
Install
One command installs Metaplate's complete framework-neutral renderer stack:
npm install metaplateMetaplate needs Node.js 20 or newer. With npm 7+ (and pnpm 8+), that also
installs compatible versions of satori, @resvg/resvg-js, and react
automatically, deduplicated against compatible versions already in the
application. Package managers that do not install peers, such as Yarn, need
them added explicitly:
yarn add metaplate satori @resvg/resvg-js reactDependency behavior
next and @resvg/resvg-wasm are the optional peers: Next applications
already own their framework version, non-Next applications should not download
it, and only metaplate/edge needs the WebAssembly renderer. The
renderer peers are bounded to the release-tested major/minor lines instead of
silently accepting unknown breaking releases.
Dependencies load only when their entry point renders, so importing metadata
helpers does not initialize Satori or Resvg's native binding. Plain
{ type, props } authoring also remains independent of React APIs and React
types; the React runtime is included by the install so JSX works immediately.
metaplate/next no longer re-exports ImageResponse. Import it from next/og
directly if a plate needs it:
import { ImageResponse } from "next/og";Quick start
Define the design once with createNodeOg. The component is Satori-compatible
JSX, not browser DOM: containers with more than one child use flex, and a root
that fills the card with padding needs boxSizing: "border-box" (see
plate constraints). The font comes from
Fontsource, so install it too: npm install @fontsource/inter (see the
fonts guide).
// src/lib/og.tsx
import { fontsourceFontLoader } from "metaplate/fonts";
import { createNodeOg } from "metaplate/node";
export const og = createNodeOg<{ title: string; alt: string }>({
alt: (copy) => copy.alt,
fonts: fontsourceFontLoader([{ font: "inter", weight: 700 }]),
headers: { "Cache-Control": "public, max-age=86400" },
component: (copy) => (
<div
style={{
boxSizing: "border-box",
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
background: "#111",
color: "#fff",
fontFamily: "Inter",
fontSize: 72,
padding: 72,
}}
>
{copy.title}
</div>
),
});The same plate renders bytes, a complete artifact with its metadata, and Fetch API responses for any framework route:
const png: Uint8Array = await og.render(copy);
const svg: string = await og.renderSvg(copy);
const artifact = await og.artifact("/posts/hello", copy);
const response: Response = await og.response(copy);
const fetchable = og.fetchableFrom((request: Request) => ({
title: new URL(request.url).pathname,
alt: "Social card",
}));Where to go next
- A static Next.js site? Static Next.js sites takes you from the first file to the post-deploy check, with a complete, type-checked example.
- A particular design? Card recipes covers text beside a photograph, long titles, several fonts, one card per record, and more.
- Making sure a card never ships broken? Testing your cards shows which check catches what, including an offline audit of a whole export.
- On an older version? Upgrading lists each breaking change and how to compare your cards before and after.
- Looking up an option or method? The plate reference lists them all, and the API reference lists every export.
Guides
Each guide below is a summary; the linked page has the full reference.
Rendering and routes
render returns PNG by default, and an output encoder produces JPEG, WebP,
or anything else from the same raw pixels. handler and handlerFrom plug
into Fetch-based routes in Next.js, Astro, React Router, and SvelteKit, and
render feeds Express or a build script. plate.cached() renders each
distinct copy once, keyed by the plate's design and the copy, answers
If-None-Match with 304 Not Modified, and keeps a signed route signed. Plates are Satori layouts, not DOM,
so read the plate constraints before designing one. Opt-in render guards
fail or report a card with text no font covers, text laid out off the canvas,
or (Node and edge) a single-color image, instead of shipping it; they are off
by default and unavailable through metaplate/next.
Rendering guide
Static files with metaplate render
metaplate render renders a whole site's cards for static hosting. It loads a
module whose default export is { plate, pages: [{ route, copy }] }, writes
every page at every size to the path its metadata URL names, verifies each
file, and records them in a checksummed manifest. --target also fails a card
that breaks a platform's rules, such as WhatsApp's 600 KB limit. --incremental
re-renders only the pages whose copy, plate, or installed renderer changed.
npx metaplate render og.render.mjs --out publicLive preview with metaplate preview
metaplate preview serves a local gallery of the same render module: every
page at every size with its metadata and --target checks, a switch for
Satori's layout debug boxes, and 1.91:1, 2:1, and 1:1 crop overlays. Saving
the module or a local file it imports reloads every open gallery.
npx metaplate preview og.render.mjsSigned image routes
A dynamic image route can refuse every URL your pages did not emit.
plate.signedMetadata(route, copy, { secret }) signs each image URL with
HMAC-SHA-256 as an extra path segment, and
plate.handlerFrom(resolve, { signed: { secret } }) answers an unsigned or
tampered path with 403 before resolving copy or rendering. It runs on Web
Crypto in Node and on the edge, supports secret rotation, and is opt-in;
static files need none of it.
Signed routes guide
Framework and runtime support
Every supported framework and runtime is listed with the official reference it follows and the release evidence behind it, from packed-tarball fixtures to dated hosted deployments. Support matrix
Edge runtimes
metaplate/edge renders the same plates through @resvg/resvg-wasm with no
Node built-ins. It runs on Deno-based edges such as Netlify Edge Functions and
Deno Deploy, and not on Cloudflare Workers or Vercel Edge, which forbid the
run-time WebAssembly compilation Satori needs.
Edge guide
Next.js
metaplate/next renders through Next's own next/og pipeline and keeps the
same route and metadata pattern. The guide also covers static export with
basePath and JPEG or WebP cards through the Node renderer.
Next.js guide
Square and landscape variants
One plate can declare variants such as a 1080x1080 square beside the
1200x630 card. The component receives { variant, size } and lays out each
size itself, and plate.variant(name) gives the full plate API for one size.
Variants guide
Metadata and meta tags
The root metaplate entry builds Open Graph and X metadata with no renderer
or framework, including per-channel images and Open Graph video and audio.
socialMetaTags and socialMetaHtml emit the <meta> tags for pages that
write their own <head>, including validated og:locale, article:, and
Discord accent-color and timestamp tags. An opt-in version, or a plate's
version: "copy", adds a ?v= token to published image URLs so crawlers that
cache images by URL fetch a changed card, while file paths stay unchanged.
Metadata guide
X player and app cards
twitter.card can be "player" or "app", with the payload each one
requires checked against X's published rules. nextTwitterMetadata maps them
to Next.js metadata.
X cards guide
Social compatibility profiles
socialImageCompatibility checks an image descriptor against each platform's
published preview rules, such as LinkedIn's minimum size, Discord's accepted
formats, or WhatsApp's 600 KB limit, without network requests. Platforms that
publish no rules, such as Instagram and Telegram, are reported as unknown.
Profiles guide
Teams, Slack, and Discord previews
Chat clients build link previews from the same meta tags, so no app is needed.
For bots and webhooks, socialImageAdaptiveCard builds an Adaptive Card from
the same metadata.
Chat previews guide
Schema.org image metadata
socialImageJsonLd describes the same images as a schema.org ImageObject
in JSON-LD, and socialImageJsonLdScript serializes it safely for a
<script> tag.
JSON-LD guide
Fonts
Satori needs the bytes of every face it renders. Loaders read them from
project files, Fontsource, other npm packages, or bytes you supply, and
memoize one copy for every render. The languageFonts plate option draws
Japanese, Arabic, Hebrew, and other scripts the plate's fonts lack, offline:
fontsourceLanguageFontLoader reads only the Noto subset files the copy needs
from installed Fontsource packages.
Fonts guide
Emoji
The emoji plate option draws emoji as images instead of missing-glyph boxes.
packageEmojiLoader from metaplate/fonts reads them offline from a set the
application installs, such as @twemoji/svg, and emojiLoader from
metaplate/emoji takes any source, including your own origin on an edge
runtime. Metaplate bundles no artwork; the guide covers each set's license.
Emoji guide
Verifying images
metaplate verify checks generated files for dimensions, format, structure,
and platform rules, and metaplate verify-url runs the same checks on a
deployed URL under a hardened fetch policy. metaplate verify-page audits a
whole page: it reads the page's og:image, twitter:card, twitter:image,
and JSON-LD images and verifies every image they name. verify takes files,
directories, and globs, and --require-content decodes PNG and JPEG and fails
an image whose every pixel is the same color. --baseline DIR compares each
card with a saved copy and fails one that changed, with a side-by-side diff
image, so a Satori or font upgrade cannot change cards unnoticed. runCli
from metaplate/cli runs any command from a script without a shell. In code,
verifySocialImage checks bytes against their published descriptor, and its
checkUrl: false skips the HTTPS rule for a local or CI build that is never
crawled.
Verification guide and
page audit guide
Deployment routes
Use a real .png or .jpg file on static hosts, the provider's Node runtime
for metaplate/node, and metaplate/edge on Deno-based edges. Hosted
certifications cover Vercel Functions, Next.js on Vercel, Netlify Functions,
Netlify Edge Functions, and Deno Deploy, each from a dated deployment whose
sources are in examples/hosted. The current status of
every route is in deployment-evidence.json.
Deployment guide
Entry points
metaplate— framework-free paths, dimensions, Open Graph and X metadata (including video and audio, and X player and app cards), cache-busting image URL versions (socialImageVersion),<meta>tag serialization, Schema.org JSON-LD, Adaptive Cards, social compatibility profiles, and byte-level social image verification.metaplate/render— Satori-based SVG generation, with themissingGlyphsandoverflowrender guards (RenderGuardError). Satori is installed automatically.metaplate/node— SVG, PNG, raw pixels, and any format a supplied encoder produces, plus Fetch API responses, with every render guard includingblank. Satori and Resvg are installed automatically. Also the hardened remote fetcher behindverify-urlandverify-page(fetchRemoteImage,verifyRemoteImage,verifyRemotePage,extractPageSocialMetadata).metaplate/edge— PNG, raw pixels, and Fetch API responses (with every render guard) through@resvg/resvg-wasmfor Deno-based edges such as Netlify Edge Functions, and other runtimes that allow run-time Wasm compilation; not Cloudflare Workers. No Node built-ins. Needs@resvg/resvg-wasmand the caller-supplied@resvg/resvg-wasm/index_bg.wasmandsatori/layout.wasmmodules.metaplate/next— native Next.jsImageResponseadapter,nextTwitterMetadatafor X player and app cards in Next metadata, andnextPageMetadata/nextPageViewportfor page-level tags. Needsnext.metaplate/font-data— runtime-neutral normalization and memoization for application/framework-provided bytes, andlanguageFontLoaderfor per-language fallback fonts from any byte source. No peers or Node built-ins.metaplate/fonts— project-file, Fontsource, generic npm-package, and application-byte font loading, per-language fallback fonts from installed Fontsource packages (fontsourceLanguageFontLoader), plus installed emoji packages (packageEmojiLoader), for Node-compatible runtimes. No peers.metaplate/emoji— runtime-neutral emoji images for theemojiplate option (emojiLoader,twemojiName). No peers or Node built-ins; the installed-package reader,packageEmojiLoader, is inmetaplate/fonts.metaplate/png— PNG header inspection and dimension verification. No peers.metaplate/image— dimension and structural verification for SVG, PNG, JPEG, WebP, and GIF, andimageAsset/imageAssetLoaderfor embedding checked image bytes in a plate. No peers or Node built-ins.metaplate/assets— Node readers for plate images: project files (readImageAsset,fileImageLoader) and remote images fetched only from allowlisted origins (remoteImageLoader). No renderer peers.metaplate/cli—runCli(args)runs anymetaplatecommand in-process and resolves to its exit code, for build scripts that would otherwise spawn the binary through a shell.
API reference lists every export by entry point, with the guide for each and the constants no guide covers.
License
MIT
