---
title: "Next.js"
description: "Generate dynamic OG images and on-demand graphics in Next.js with the Imejis.io API. A secure App Router pattern that keeps your API key server-side and returns a cached image."
url: "https://www.imejis.io/apis/nextjs"
published: "2026-07-27"
---

# Next.js

## Next.js

Generate a unique OG image for every page, or any on-demand graphic, from a single Imejis template. The pattern below uses a Next.js **Route Handler** as a thin proxy: your API key stays on the server, and the browser (and social crawlers) only ever see your own URL.

Set two environment variables first: `IMEJIS_API_KEY` (from your dashboard) and `IMEJIS_DESIGN_ID` (any design's ID).

```bash title=".env.local"
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id
```

### 1. A route handler that renders the image

This handler takes a `title` query param, POSTs it to Imejis as a `component.property` override, and streams the finished image back. Because the key is sent server-side, it never reaches the client.

```tsx showLineNumbers title="app/og/route.ts"
import { NextRequest } from "next/server"

export async function GET(req: NextRequest) {
  const title = req.nextUrl.searchParams.get("title") ?? "Untitled"

  const res = await fetch(
    `https://render.imejis.io/v1/${process.env.IMEJIS_DESIGN_ID}`,
    {
      method: "POST",
      headers: {
        "dma-api-key": process.env.IMEJIS_API_KEY as string,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ "headline.text": title }),
    }
  )

  return new Response(res.body, {
    headers: {
      "Content-Type": "image/jpeg",
      // Renders are deterministic, so cache aggressively at the edge/CDN.
      "Cache-Control": "public, immutable, no-transform, max-age=31536000",
    },
  })
}
```

### 2. Point your metadata at it

Reference the handler from `generateMetadata`, passing each page's real data. Social platforms fetch `/og?title=…` from your own domain, no key exposed.

```tsx showLineNumbers title="app/blog/[slug]/page.tsx"
import type { Metadata } from "next"

export async function generateMetadata({
  params,
}: {
  params: { slug: string }
}): Promise<Metadata> {
  const post = await getPost(params.slug)
  const ogImage = `https://your-site.com/og?title=${encodeURIComponent(
    post.title
  )}`

  return {
    title: post.title,
    openGraph: { images: [ogImage] },
    twitter: { card: "summary_large_image", images: [ogImage] },
  }
}
```

**Override any field.** Each dynamic component exposes keys like `headline.text`, `title.color`, or `image.image`. Send only the ones you want to change, and add more query params to the handler as needed. You'll find a design's overridable fields in its Share / API panel.

For a non-sensitive, public design you can skip the proxy and point `og:image` straight at the render URL with the key as a query param, but the proxy keeps your key private, which is the right default.
