---
title: "OG Images"
description: "Dynamic and static Open Graph images for docs — how they work, what context is passed, and how the docs website generates them"
canonical_url: "https://docs.farming-labs.dev/docs/customization/og-images"
markdown_url: "https://docs.farming-labs.dev/docs/customization/og-images.md"
last_updated: "2018-10-20"
agent:
  tokenBudget: 850
  task: "Configure and verify static or dynamic Open Graph images for docs pages."
  outcome: "Every tested docs page resolves an absolute OG image URL whose response is a valid social preview image."
  appliesTo:
    framework:
      - "nextjs"
    version:
      - ">=0.2.60"
    package:
      - "@farming-labs/docs"
      - "@farming-labs/next"
  prerequisites:
    - "The docs site has an absolute public base URL available to metadata generation."
    - "Choose dynamic generation for page-aware images or commit a static image for a fixed preview."
  files:
    - "docs.config.ts"
    - "app/api/og/route.ts"
    - "public/og.png"
  sideEffects:
    - "Dynamic generation adds an image endpoint that runs when crawlers request a preview."
  verification:
    - description: "Fetch the generated metadata and its og:image URL for a representative docs page."
      expect: "The URL is absolute and the image request returns HTTP 200 with an image content type."
  rollback:
    - "Restore the previous og configuration and remove the dynamic route or static image introduced by the change."
  failureModes:
    - symptom: "Social previews use a relative or unreachable image URL."
      resolution: "Configure an absolute sitemap, llms.txt, robots, AI docs, or site base URL and retest generated metadata."
    - symptom: "The dynamic image route throws while rendering."
      resolution: "Limit the image component to fonts, layout, and CSS supported by the selected image renderer."
---

# OG Images
URL: /docs/customization/og-images
LLM index: /llms.txt
Description: Dynamic and static Open Graph images for docs — how they work, what context is passed, and how the docs website generates them
Related: /docs/configuration, /docs/reference, /docs/themes

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Configure and verify static or dynamic Open Graph images for docs pages.
Outcome: Every tested docs page resolves an absolute OG image URL whose response is a valid social preview image.

### Applies To

- Framework: `nextjs`
- Version: `>=0.2.60`
- Package: `@farming-labs/docs`, `@farming-labs/next`

### Prerequisites

- The docs site has an absolute public base URL available to metadata generation.
- Choose dynamic generation for page-aware images or commit a static image for a fixed preview.

### Files

- `docs.config.ts`
- `app/api/og/route.ts`
- `public/og.png`

### Side Effects

- Dynamic generation adds an image endpoint that runs when crawlers request a preview.

### Verification

- Fetch the generated metadata and its og:image URL for a representative docs page.
  - Expected: The URL is absolute and the image request returns HTTP 200 with an image content type.

### Rollback

- Restore the previous og configuration and remove the dynamic route or static image introduced by the change.

### Failure Modes

- Social previews use a relative or unreachable image URL. — Recovery: Configure an absolute sitemap, llms.txt, robots, AI docs, or site base URL and retest generated metadata.
- The dynamic image route throws while rendering. — Recovery: Limit the image component to fonts, layout, and CSS supported by the selected image renderer.
<!-- farming-labs:agent-contract:end -->

# OG Images

For Next.js dynamic previews, set `og.enabled`, `og.type: "dynamic"`, and `og.endpoint` in
`docs.config.ts`, then implement `GET` in `app/api/og/route.tsx` when the response contains JSX.
Read the `title` and `description` query parameters and return a 1200x630 image. For a reliable
per-page override, set explicit `openGraph` and `twitter` frontmatter.

Fetch a representative page's absolute `og:image` URL and require HTTP 200 with an image content
type. If a dynamic image is stale, hard-refresh or add a cache-busting query. If rendering throws,
limit the route to fonts, layout, and CSS supported by `ImageResponse`; restore the previous `og`
config and remove the new route or static image to roll back.

Open Graph (OG) images are the preview images shown when your docs pages are shared on social platforms, Slack, or messaging apps. This guide explains how the framework supports **dynamic** (generated per page) and **static** (per-page image file) OG images, what data your image generator receives, and how the docs website implements dynamic OG.

## Overview: static vs dynamic

<Tabs items={["Dynamic", "Static"]}>
  <Tab value="Dynamic">
    One design for all pages; title and description change per page. You implement an API route. The framework calls it with the page’s `title` and `description` (from frontmatter) as query params and uses the returned image URL for `og:image` and `twitter:image`.
  </Tab>
  <Tab value="Static">
    Custom image per page (e.g. diagrams, screenshots). Set `openGraph` and/or `twitter` (or the shorthand `ogImage`) in that page’s frontmatter. The framework uses those values instead of calling the dynamic endpoint.
  </Tab>
</Tabs>

You can mix both: use a dynamic endpoint by default and override with static images on specific pages via frontmatter.

---

## How dynamic OG works

1. **You configure** an OG endpoint in `docs.config.ts`:
   - `og: { enabled: true, type: "dynamic", endpoint: "/api/og" }`
2. **For each docs page**, the framework builds a **single URL** that includes the page’s context:
   - **Next.js (MDX):** A remark plugin injects that URL into the page’s exported `metadata` (from frontmatter). The URL is your endpoint plus query params.
   - **Query params** the framework adds:
     - `title` — the page’s `title` from frontmatter (required).
     - `description` — the page’s `description` from frontmatter (optional).
3. **Your API route** receives a GET request with those query params and returns a **1200×630** image (e.g. PNG).
4. The framework sets that URL as `og:image` and `twitter:image` in the page metadata, so crawlers and social platforms fetch the image when the page is shared.

So your image generator gets **full context**: the same `title` and `description` that appear in the doc and in meta tags. You can render them (and any other logic) inside your image.

---

## What context your image generator receives

When the framework calls your dynamic OG endpoint, it passes:

| Query param   | Source           | Example value        |
| ------------- | ----------------- | -------------------- |
| `title`       | Page frontmatter `title` | `Introduction` or `OG Images` |
| `description` | Page frontmatter `description` | `Dynamic and static Open Graph images for docs.` |

The URL looks like:

```text
https://your-site.com/api/og?title=Introduction&description=Get+started+with+the+framework.
```

Your route reads `searchParams.get("title")` and `searchParams.get("description")` and uses them when rendering the image (e.g. in `ImageResponse`). That’s the full context the framework provides; you can add more query params in your own links if needed.

---

## Example: how the docs website uses dynamic OG

The `@farming-labs/docs` website which uses **Nextjs** uses a dynamic OG endpoint so every docs page gets a unique preview image with that page’s title and description.

### 1. Config (`docs.config.ts`)

```ts title="1-config-docs-config-ts.ts"
og: {
  enabled: true,
  type: "dynamic",
  endpoint: "/api/og",
},
```

No other OG config is required; the framework builds the URL for each page and injects it into metadata.

### 2. API route (`app/api/og/route.ts`)

The route receives `title` and `description` from the query string and returns a 1200×630 image using Next.js `ImageResponse`:

```tsx title="api/og/route.ts"
import { ImageResponse } from "next/og";
import { NextRequest } from "next/server";

export const runtime = "edge";

export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url);
  const title = searchParams.get("title") ?? "@farming-labs/docs";
  const description = searchParams.get("description") ?? "";

  return new ImageResponse(
    (
      <div
        style={{
          width: "100%",
          height: "100%",
          display: "flex",
          flexDirection: "column",
          justifyContent: "space-between",
          backgroundColor: "#000000",
          padding: "50px 80px",
          // ... fonts, branding, borders
        }}
      >
        {/* Branding strip */}
        <span style={{ fontFamily: "monospace", color: "rgba(255,255,255,0.4)" }}>
          @farming-labs/docs
        </span>

        {/* Page context: title and description from frontmatter */}
        <div>
          <h1 style={{ fontSize: 68, fontWeight: 700, color: "#fff" }}>
            {title}
          </h1>
          {description && (
            <p style={{ fontSize: 22, color: "rgba(255,255,255,0.4)" }}>
              {description}
            </p>
          )}
        </div>

        {/* Footer: site label + CTA */}
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          <span>documentation · docs.farming-labs.com</span>
          <span>get started →</span>
        </div>
      </div>
    ),
    { width: 1200, height: 630 }
  );
}
```

So the image generator has **full context**: the same `title` and `description` that come from the MDX frontmatter. The live site loads Inter and JetBrains Mono for typography and uses the same layout with diagonal pattern and borders; the only variables are `title` and `description`.

### 3. What each page gets

- **Introduction** (`/docs`) → `/api/og?title=Introduction&description=...`
- **OG Images** (`/docs/customization/og-images`) → `/api/og?title=OG+Images&description=Dynamic+and+static+...`
- **API Reference** (`/docs/reference`) → `/api/og?title=API+Reference&description=...`

Each URL is set as that page’s `og:image` and `twitter:image`, so shares show the correct title and description on the card.

---

## Using static OG for specific pages

To use a **static image** for a single page (e.g. a pre-made graphic or screenshot), set `openGraph` and `twitter` in that page’s frontmatter. The framework will use those instead of calling the dynamic endpoint for that page.

```md
--- 
title: "Docs Title"
description: "The description of the title goes here."
openGraph:
  images:
    - url: "/og/path-to-image/image.png"
      width: 1200
      height: 630
twitter:
  card: "summary_large_image"
  images:
    - "/og/path-to-image/image.png"
---
```

Shorthand: you can set only `ogImage: "/og/some-page.png"` and the framework will use it for both Open Graph and Twitter unless you override with full `openGraph` / `twitter`. See [Page Frontmatter](/docs/reference#page-frontmatter) for all options.

---

## Configuration reference

| Option        | Type     | Description |
| ------------- | -------- | ----------- |
| `og.enabled`  | `boolean`| Turn OG/twitter images on or off. |
| `og.type`     | `"static" \| "dynamic"` | Use a fixed image or a dynamic endpoint. |
| `og.endpoint` | `string`| Path to your OG API route (e.g. `"/api/og"`). Required for dynamic. |
| `og.defaultImage` | `string` | Fallback OG image URL when no page-specific OG is set. |

Full details: [OGConfig](/docs/reference#ogconfig) in the API reference.

---

## Testing your OG image

1. Start your dev server (e.g. `pnpm dev`).
2. Open the endpoint with sample params in the browser:
   - `http://localhost:3000/api/og?title=Test+Page&description=My+description`
3. Confirm the image shows the right title and description.
4. After code changes, do a hard refresh (e.g. Cmd+Shift+R) or use a cache-busting query (e.g. `&_=1`) so you see the latest image.

---

## Summary

- **Dynamic OG** gives every docs page a generated preview: the framework calls your endpoint with `title` and `description` from frontmatter, and you return a 1200×630 image. The docs website uses this with a single `/api/og` route.
- **Static OG** lets you set a specific image per page via frontmatter (`openGraph`, `twitter`, or `ogImage`), which overrides the dynamic endpoint for that page.
- Your image generator receives the **full context** it needs: the page’s `title` and `description` as query parameters, so you can render them (and anything else you derive from them) in your image.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
