Better OG

Introduction

Platform-aware Open Graph rendering with request resolution, adaptive layout, and multi-runtime adapters.

What is better-og?

better-og provides a set of utilities for generating Open Graph images that automatically adapt to the platform requesting them. Whether a link is shared on Twitter, Telegram, Slack, iMessage, or Instagram, better-og detects the caller and renders the image at the optimal aspect ratio.

Key Features

  • Multi-signal platform detection -- uses query overrides, User-Agent, Referer, and other request hints to identify the requesting platform.
  • Adaptive layout primitives -- exposes computed safe areas, reusable layout boxes, and platform-native layout strategies.
  • Script-aware font resolution -- built-in Google Fonts integration with automatic fallback fonts for en, ja, ko, zh, ar, hi, ru, and uk.
  • Multi-runtime adapters -- works with Next.js (Node & Edge), Cloudflare Workers, and TanStack Start.
  • Multiple renderers -- choose between next/og (Vercel's built-in Satori) or Takumi for WASM-based rendering.

Aspect Ratio Presets

PresetDimensionsRatioUsed by
STANDARD1200 x 6301.91 : 1Twitter, Generic
SQUARE1200 x 12001 : 1Telegram, Slack
PORTRAIT630 x 12001 : 1.91iMessage
INSTAGRAM1200 x 15004 : 5Instagram

How It Works

  1. A middleware intercepts OG image requests and resolves the platform-aware render plan.
  2. The request plan normalizes platform, aspect_ratio, and layout for the downstream OG route.
  3. The OG route handler loads the required fonts, computes layout boxes, and renders with the selected runtime adapter.

Installation

Install only the runtime pieces you need.

pnpm add @better-og/core next react

First Route (Next.js)

Create your first OG route in a Next.js app:

app/og/[lang]/route.tsx
import { resolveFontSetup } from "@better-og/core";
import {
  createOgRouteHandler,
  loadGoogleFontForImageResponse,
} from "@better-og/next";

export const runtime = "nodejs";
export const revalidate = false;

const fontSetup = await resolveFontSetup({
  baseFonts: await loadGoogleFontForImageResponse({
    family: "Geist",
    weights: [400, 700],
  }),
  fallbackLocales: ["ja"],
});

export const GET = createOgRouteHandler({
  component: (ogContext) => (
    <div
      style={{
        display: "flex",
        width: "100%",
        height: "100%",
        alignItems: "center",
        justifyContent: "center",
        fontFamily: fontSetup.families.base,
        paddingBottom: 32 + ogContext.safeArea.bottom,
        background: "linear-gradient(135deg, #0d192c, #174a70, #4993d6)",
        color: "white",
      }}
    >
      <div style={{ fontSize: 64, fontWeight: 700 }}>Hello from better-og</div>
    </div>
  ),
  baseFonts: fontSetup.fonts,
});

Add the Proxy

To enable automatic aspect-ratio detection, add a proxy:

proxy.ts
import { withOgRewrite } from "@better-og/next";
import type { NextRequest } from "next/server";

export async function proxy(request: NextRequest) {
  return await withOgRewrite(request);
}

export const config = {
  matcher: ["/og/:path*"],
};

Next.js Config

If using the Takumi renderer, keep it external:

next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  serverExternalPackages: ["@takumi-rs/image-response"],
};

export default nextConfig;
Edit on GitHub

Last updated on

On this page