Next.js icon

Next.js

NextjsNext.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).

.env.local
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id

1 a route handler that renders the image1. 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.

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 it2. 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.

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.