og-images

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Creating Share Images for Next.js

为Next.js创建分享图片

Generate dynamic OpenGraph (1200x630) and Twitter (1200x600) images using
next/og
ImageResponse.
使用
next/og
的ImageResponse生成动态OpenGraph(1200x630)和Twitter(1200x600)图片。

Choosing an Approach

选择实现方式

  • File-based route (
    app/opengraph-image.tsx
    ): Best for static pages with known titles at build time. Export
    runtime
    ,
    alt
    ,
    size
    ,
    contentType
    , and a default
    Image
    function.
  • API route (
    app/api/og/route.tsx
    ): Best for dynamic content (blog posts, CMS). Accept
    slug
    and/or
    title
    as query params. Reference in metadata via
    generateMetadata()
    .
Use
export const runtime = "edge"
for both approaches.
  • 基于文件的路由
    app/opengraph-image.tsx
    ):最适合构建时标题已知的静态页面。导出
    runtime
    alt
    size
    contentType
    以及默认的
    Image
    函数。
  • API路由
    app/api/og/route.tsx
    ):最适合动态内容(博客文章、CMS内容)。接受
    slug
    和/或
    title
    作为查询参数。通过
    generateMetadata()
    在元数据中引用。
两种方式都需使用
export const runtime = "edge"

File Naming Convention

文件命名规范

FilePurposeDimensions
opengraph-image.tsx
Facebook, LinkedIn, iMessage1200x630
twitter-image.tsx
Twitter/X cards1200x600
app/api/og/route.tsx
Dynamic API route1200x630
Place file-based routes in the relevant route directory (e.g.,
app/about/opengraph-image.tsx
for
/about
).
文件用途尺寸
opengraph-image.tsx
Facebook、LinkedIn、iMessage1200x630
twitter-image.tsx
Twitter/X卡片1200x600
app/api/og/route.tsx
动态API路由1200x630
将基于文件的路由放在对应路由目录下(例如,
app/about/opengraph-image.tsx
对应
/about
页面)。

Layout Pattern

布局模式

  • Use
    flexDirection: "column"
    with
    justifyContent: "space-between"
    to separate content from branding
  • Title and subtitle (e.g., author name) go top-left in a stacked flex column
  • Title: large, bold/medium weight, dark color
  • Subtitle: same size or smaller, lighter weight, muted color (e.g.,
    #888
    )
  • Keep text left-aligned with
    textWrap: "balance"
    and constrain width with
    maxWidth
  • Use
    letterSpacing: "-0.02em"
    for tight, editorial feel at large sizes
  • Padding:
    48px
    on all sides works well at 1200x630
  • 使用
    flexDirection: "column"
    搭配
    justifyContent: "space-between"
    将内容与品牌标识分离
  • 标题和副标题(如作者名)放在左上角的堆叠flex列中
  • 标题:大字号、加粗/中等字重、深色
  • 副标题:字号相同或更小、更轻字重、柔和颜色(例如
    #888
  • 使用
    textWrap: "balance"
    保持文本左对齐,并通过
    maxWidth
    限制宽度
  • 大字号文本使用
    letterSpacing: "-0.02em"
    以营造紧凑的编辑风格
  • 内边距:1200x630尺寸下,四边设置
    48px
    内边距效果良好

Avatar / Logo (Optional)

头像/Logo(可选)

If the project has an avatar or logo, place it in the bottom-right corner using a flex container with
justifyContent: "flex-end"
. Load images via
fetch
+
arrayBuffer
, convert to base64 data URI for the
src
. Use
borderRadius: "50%"
for circular avatars. Cache loaded assets in a
Map
to avoid refetching.
如果项目有头像或Logo,使用
justifyContent: "flex-end"
的flex容器将其放在右下角。通过
fetch
+
arrayBuffer
加载图片,转换为base64数据URI作为
src
。圆形头像使用
borderRadius: "50%"
。将加载的资源缓存到
Map
中避免重复请求。

Custom Fonts

自定义字体

Load
.ttf
files from
public/fonts/
using
new URL("../../../public/fonts/YourFont.ttf", import.meta.url)
. Pass the
ArrayBuffer
to
ImageResponse
via the
fonts
option. Cache the font buffer after first load. Match the
weight
in the fonts config to the actual font file weight.
public/fonts/
加载
.ttf
文件,使用
new URL("../../../public/fonts/YourFont.ttf", import.meta.url)
。通过
fonts
选项将
ArrayBuffer
传递给
ImageResponse
。首次加载后缓存字体缓冲区。确保字体配置中的
weight
与实际字体文件的字重匹配。

Title Case

标题大小写规则

If titles come from a CMS, apply smart title case:
  • Lowercase small words (a, an, the, and, but, for, in, of, etc.) unless first or last
  • Always capitalize brand names correctly (WordPress, JavaScript, GitHub, macOS, etc.)
  • Uppercase known acronyms (AI, API, CSS, HTML, UI, UX)
  • Handle hyphenated words by capitalizing each part independently
如果标题来自CMS,应用智能标题大小写:
  • 小写虚词(a、an、the、and、but、for、in、of等),除非位于句首或句尾
  • 品牌名称始终正确大写(WordPress、JavaScript、GitHub、macOS等)
  • 已知缩写全部大写(AI、API、CSS、HTML、UI、UX)
  • 连字符连接的单词,每个部分独立大写

Metadata Integration

元数据集成

Reference the OG route in
generateMetadata()
:
tsx
export function generateMetadata({ params }) {
  return {
    openGraph: {
      images: [`/api/og?slug=${params.slug}`],
    },
  };
}
For static pages, pass the title directly:
/api/og?title=About
.
generateMetadata()
中引用OG路由:
tsx
export function generateMetadata({ params }) {
  return {
    openGraph: {
      images: [`/api/og?slug=${params.slug}`],
    },
  };
}
对于静态页面,直接传递标题:
/api/og?title=About

Satori Rules

Satori规则

These are hard requirements of the
next/og
rendering engine (Satori):
  1. Every element needs
    display: "flex"
    — this is the only layout mode
  2. Inline styles only — no CSS classes, no external stylesheets, no CSS variables
  3. All text must be in elements with explicit style props
  4. Use hex colors — no
    rgb()
    ,
    hsl()
    , or CSS variables
  5. No
    gap
    on older versions
    — test before relying on it; fallback to margin
这些是
next/og
渲染引擎(Satori)的硬性要求:
  1. 每个元素都需要
    display: "flex"
    —— 这是唯一的布局模式
  2. 仅支持内联样式 —— 不支持CSS类、外部样式表、CSS变量
  3. 所有文本必须放在带有显式style属性的元素中
  4. 使用十六进制颜色 —— 不支持
    rgb()
    hsl()
    或CSS变量
  5. 旧版本不支持
    gap
    —— 使用前先测试;可回退使用margin

Common Issues

常见问题

IssueSolution
Text not renderingAdd
display: "flex"
to the text wrapper
Layout brokenEnsure all containers have
display: "flex"
Colors wrongUse hex colors, not CSS variables
Font not loadingCheck the relative path from route file to
public/fonts/
Image not showingConvert to base64 data URI, don't use relative paths
问题解决方案
文本未渲染为文本容器添加
display: "flex"
布局错乱确保所有容器都设置了
display: "flex"
颜色显示错误使用十六进制颜色,而非CSS变量
字体未加载检查路由文件到
public/fonts/
的相对路径
图片不显示转换为base64数据URI,不要使用相对路径

Testing

测试

Preview during development by visiting the route directly in the browser:
http://localhost:3000/api/og?title=Hello+World
After building, verify routes register as dynamic (
f
prefix) in the build output.
开发期间可直接在浏览器访问路由预览:
http://localhost:3000/api/og?title=Hello+World
构建完成后,验证路由在构建输出中注册为动态路由(前缀为
f
)。