Directus Open Graph Tags: Add OG Meta to Your Headless CMS Site

How to add og:title, og:image, and og:description to Directus-powered sites — with custom fields, REST API queries, and Next.js generateMetadata.

How Directus handles OG metadata

Directus is a headless CMS with a REST and GraphQL API. Like KeystoneJS and Contentful, it doesn't render HTML — so OG tags are generated by your frontend (Next.js, Nuxt, SvelteKit, etc.) using data from the Directus API.

The recommended pattern is to add dedicated OG fields to your content collections in Directus, then query those fields in your frontend's metadata layer. This gives editors full control over social preview appearance.

Step 1: Add SEO fields to your Directus collection

In the Directus admin panel, add these fields to your content collection (e.g., posts):

  • seo_title — Input (String) — fallback to title
  • seo_description — Textarea (String) — fallback to excerpt
  • seo_image — Image (UUID) — Directus file relation

Using a Directus M2O relation to the directus_files table for the image field lets you leverage Directus's built-in image transformation API (crop, resize, format) to ensure the correct 1200×630 dimensions.

Step 2: Query OG data from the Directus REST API

// lib/directus.ts
const DIRECTUS_URL = process.env.DIRECTUS_URL!;
const DIRECTUS_TOKEN = process.env.DIRECTUS_TOKEN!;

export async function getPost(slug: string) {
  const res = await fetch(
    `${DIRECTUS_URL}/items/posts?filter[slug][_eq]=${slug}&fields[]=title,excerpt,content,seo_title,seo_description,seo_image.*&limit=1`,
    {
      headers: {
        Authorization: `Bearer ${DIRECTUS_TOKEN}`,
      },
      next: { revalidate: 60 },
    }
  );
  
  const { data } = await res.json();
  return data[0] || null;
}

// Directus image URL with transformation
export function directusImageUrl(
  fileId: string,
  width = 1200,
  height = 630
): string {
  return `${DIRECTUS_URL}/assets/${fileId}?width=${width}&height=${height}&format=webp&quality=85`;
}

Step 3: Generate metadata in Next.js

// app/posts/[slug]/page.tsx
import type { Metadata } from "next";
import { getPost, directusImageUrl } from "@/lib/directus";

export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}): Promise<Metadata> {
  const post = await getPost(params.slug);
  
  if (!post) return { title: "Not Found" };
  
  const title = post.seo_title || post.title;
  const description = post.seo_description || post.excerpt;
  const imageUrl = post.seo_image?.id
    ? directusImageUrl(post.seo_image.id)
    : undefined;
  
  return {
    title,
    description,
    openGraph: {
      title,
      description,
      type: "article",
      images: imageUrl
        ? [{ url: imageUrl, width: 1200, height: 630 }]
        : [],
    },
    twitter: {
      card: "summary_large_image",
      title,
      description,
      images: imageUrl ? [imageUrl] : [],
    },
  };
}

export default async function PostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  return <article><h1>{post.title}</h1></article>;
}

Using Directus image transformations for OG

One major advantage of Directus is the built-in image transformation API. When editors upload a high-res image, you can generate an optimized 1200×630 OG crop on-the-fly:

// Directus image transformation URL parameters
const ogImage = `${DIRECTUS_URL}/assets/${fileId}?` + new URLSearchParams({
  width: "1200",
  height: "630",
  fit: "cover",       // crop to exact dimensions
  format: "webp",     // optimized format
  quality: "85",      // file size balance
}).toString();

// Result: https://your-directus.com/assets/uuid?width=1200&height=630&fit=cover&format=webp&quality=85

This eliminates the need to manually create OG-sized images — editors upload once, Directus crops to fit.

Directus Flow for automatic OG image generation

For advanced setups, use a Directus Flow (webhook trigger) to auto-generate OG images when content is published:

  • Trigger: Item Updated on the posts collection
  • Condition: status changed to "published"
  • Action: HTTP Request to your OG image generator API with the post title
  • Action: Update the seo_image field with the generated image ID

Common pitfalls with Directus OG

  • CORS on assets: Configure Directus ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION and ensure your assets URL is accessible from social crawlers
  • Authentication on assets: Use PUBLIC_ROLE permissions so social scrapers can fetch OG images without auth tokens
  • Missing fallback: Always fall back to a default OG image if seo_image is null
  • Cache headers: Set ASSETS_CACHE_TTL appropriately — too long and OG updates won't propagate

Verify your Directus OG tags live

After deploying, paste your page URL into OGFixer to see exactly how your social previews render on Twitter, LinkedIn, Discord, and Slack.

Check your OG tags free →