Remix icon

Remix

RemixRemix

Generate a unique OG image for every page, or any on-demand graphic, from a single Imejis template. The pattern below uses a Remix resource route 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
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id

1 a resource route that renders the image1. A resource route that renders the image

This route's loader takes a title query param, POSTs it to Imejis as a component.property override, and returns the finished image as a Response. Because the loader runs on the server, the key never reaches the client.

app/routes/og.tsx
import type { LoaderFunctionArgs } from "@remix-run/node"
 
export async function loader({ request }: LoaderFunctionArgs) {
  const title = new URL(request.url).searchParams.get("title") ?? "Untitled"
 
  const upstream = 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(upstream.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 resource route from a page's meta export, passing each page's real data. Social platforms fetch /og?title=… from your own domain, no key exposed.

app/routes/blog.$slug.tsx
import type { MetaFunction } from "@remix-run/node"
 
export const meta: MetaFunction = ({ data }) => {
  const ogImage = `https://your-site.com/og?title=${encodeURIComponent(
    data.title
  )}`
 
  return [
    { title: data.title },
    { property: "og:image", content: ogImage },
    { name: "twitter:card", content: "summary_large_image" },
    { name: "twitter:image", content: 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. A design's overridable fields are listed in its Share / API panel.