Nuxt icon

Nuxt

NuxtNuxt

Give every page its own OG image from one Imejis template. The Nitro server route below acts as a proxy: your API key stays on the server via runtimeConfig, and crawlers only ever hit your own URL.

Set two environment variables and expose them to the server through runtimeConfig.

.env
IMEJIS_API_KEY=your_api_key
IMEJIS_DESIGN_ID=your_design_id
nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    imejisApiKey: process.env.IMEJIS_API_KEY,
    imejisDesignId: process.env.IMEJIS_DESIGN_ID,
  },
})

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

server/routes/og.get.ts
export default defineEventHandler(async (event) => {
  const { title = "Untitled" } = getQuery(event)
  const config = useRuntimeConfig()
 
  const image = await $fetch<ArrayBuffer>(
    `https://render.imejis.io/v1/${config.imejisDesignId}`,
    {
      method: "POST",
      headers: { "dma-api-key": config.imejisApiKey as string },
      body: { "headline.text": String(title) },
      responseType: "arrayBuffer",
    }
  )
 
  setResponseHeaders(event, {
    "Content-Type": "image/jpeg",
    "Cache-Control": "public, immutable, no-transform, max-age=31536000",
  })
  return Buffer.from(image)
})

2 reference it from your page2. Reference it from your page

pages/blog/[slug].vue
<script setup lang="ts">
const { slug } = useRoute().params
const { data: post } = await useFetch(`/api/posts/${slug}`)
 
useSeoMeta({
  title: () => post.value?.title,
  ogImage: () =>
    `https://your-site.com/og?title=${encodeURIComponent(post.value?.title ?? "")}`,
  twitterCard: "summary_large_image",
})
</script>

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