Skip to content

Vue.js Integration

Generate OG images for Vue.js and Nuxt applications.

Setup

bash
# No additional packages needed

Environment Variable

bash
# .env
NUXT_OG_IMAGE_API_KEY=og_your_api_key_here

Server Route

javascript
// server/api/og.get.js
export default defineEventHandler(async (event) => {
  const query = getQuery(event);
  const config = useRuntimeConfig();
  
  const response = await fetch('https://ogimageapi.io/api/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': config.ogImageApiKey
    },
    body: JSON.stringify({
      title: query.title || 'My Site',
      subtitle: query.subtitle || '',
      template: query.template || 'default',
      theme: 'dark'
    })
  });
  
  const buffer = await response.arrayBuffer();
  
  setResponseHeaders(event, {
    'Content-Type': 'image/png',
    'Cache-Control': 'public, max-age=86400'
  });
  
  return Buffer.from(buffer);
});

Nuxt Config

javascript
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    ogImageApiKey: process.env.NUXT_OG_IMAGE_API_KEY
  }
});

Using useSeoMeta

vue
<!-- pages/blog/[slug].vue -->
<script setup>
const route = useRoute();
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`);

useSeoMeta({
  title: post.value.title,
  description: post.value.excerpt,
  ogTitle: post.value.title,
  ogDescription: post.value.excerpt,
  ogImage: `/api/og?title=${encodeURIComponent(post.value.title)}&subtitle=${encodeURIComponent(post.value.excerpt)}`,
  ogType: 'article',
  twitterCard: 'summary_large_image',
  twitterTitle: post.value.title,
  twitterImage: `/api/og?title=${encodeURIComponent(post.value.title)}`
});
</script>

<template>
  <article>
    <h1>{{ post.title }}</h1>
    <!-- Content -->
  </article>
</template>

Nuxt 2

Server Middleware

javascript
// server-middleware/og-image.js
export default async function (req, res, next) {
  if (!req.url.startsWith('/og-image')) {
    return next();
  }
  
  const url = new URL(req.url, `http://${req.headers.host}`);
  const title = url.searchParams.get('title') || 'My Site';
  const subtitle = url.searchParams.get('subtitle') || '';
  
  try {
    const response = await fetch('https://ogimageapi.io/api/generate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.OG_IMAGE_API_KEY
      },
      body: JSON.stringify({
        title,
        subtitle,
        template: 'default',
        theme: 'dark'
      })
    });
    
    const buffer = await response.arrayBuffer();
    
    res.setHeader('Content-Type', 'image/png');
    res.setHeader('Cache-Control', 'public, max-age=86400');
    res.end(Buffer.from(buffer));
  } catch (error) {
    console.error('OG image error:', error);
    res.statusCode = 500;
    res.end('Error generating image');
  }
}

Nuxt Config

javascript
// nuxt.config.js
export default {
  serverMiddleware: ['~/server-middleware/og-image.js'],
  head: {
    meta: [
      // Default OG image
      { property: 'og:image', content: '/og-image?title=My+Site' }
    ]
  }
};

Vue 3 SPA

For SPAs, generate images at build time:

Build Script

javascript
// scripts/generate-og.js
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const pages = [
  { slug: 'home', title: 'Welcome', subtitle: 'Build with Vue' },
  { slug: 'about', title: 'About', subtitle: 'Our story' }
];

async function generateImages() {
  const outputDir = path.join(__dirname, '../public/og');
  fs.mkdirSync(outputDir, { recursive: true });
  
  for (const page of pages) {
    const response = await fetch('https://ogimageapi.io/api/generate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.VITE_OG_IMAGE_API_KEY
      },
      body: JSON.stringify({
        title: page.title,
        subtitle: page.subtitle,
        template: 'default',
        theme: 'dark'
      })
    });
    
    const buffer = await response.arrayBuffer();
    fs.writeFileSync(
      path.join(outputDir, `${page.slug}.png`),
      Buffer.from(buffer)
    );
    console.log(`Generated: ${page.slug}.png`);
  }
}

generateImages();

Vue Component

vue
<!-- components/SeoHead.vue -->
<script setup>
import { useHead } from '@vueuse/head';

const props = defineProps({
  title: { type: String, required: true },
  description: { type: String, default: '' },
  image: { type: String, default: '/og/home.png' }
});

useHead({
  title: props.title,
  meta: [
    { name: 'description', content: props.description },
    { property: 'og:title', content: props.title },
    { property: 'og:description', content: props.description },
    { property: 'og:image', content: props.image },
    { property: 'og:type', content: 'website' },
    { name: 'twitter:card', content: 'summary_large_image' },
    { name: 'twitter:title', content: props.title },
    { name: 'twitter:image', content: props.image }
  ]
});
</script>

<template>
  <slot />
</template>

Usage

vue
<!-- pages/About.vue -->
<template>
  <SeoHead
    title="About Us"
    description="Learn about our team"
    image="/og/about.png"
  >
    <div class="about-page">
      <!-- Content -->
    </div>
  </SeoHead>
</template>

Composable for Dynamic OG

javascript
// composables/useOGImage.js
export function useOGImage() {
  const config = useRuntimeConfig();
  
  async function generateOGUrl(params) {
    const queryString = new URLSearchParams(params).toString();
    return `/api/og?${queryString}`;
  }
  
  async function getSignedUrl(params) {
    const response = await $fetch('/api/og/signed', {
      method: 'POST',
      body: params
    });
    return response.url;
  }
  
  return {
    generateOGUrl,
    getSignedUrl
  };
}

Best Practices

  1. Use Nuxt for SSR — Best OG support
  2. Generate at build time for static content
  3. Cache server responses — 24h minimum
  4. Use composables — Reusable logic
  5. Test with validators — Facebook, Twitter, LinkedIn

Generate stunning Open Graph images programmatically.