Prismic Open Graph Tags: Add OG Meta to Your Prismic CMS Site

How to add og:title, og:image, and og:description to Prismic-powered sites — with Image fields, slice metadata, and Next.js generateMetadata integration.

Prismic and OG metadata architecture

Prismic is a headless CMS used for marketing sites, blogs, and landing pages. Like all headless CMSs, OG tags are rendered in the frontend layer — not Prismic itself. Your Next.js, Nuxt, or SvelteKit app queries Prismic's API and generates the <meta> tags.

Prismic has a built-in concept for this: SEO fields in your Custom Type definition. The standard pattern is to add a "SEO & Social" group field with title, description, and image.

Step 1: Add SEO fields to your Prismic Custom Type

In the Prismic Custom Type editor (or customtypes/ JSON), add an SEO group:

// customtypes/blog_post/index.json (relevant section)
{
  "SEO": {
    "seo_title": {
      "type": "StructuredText",
      "config": {
        "single": "paragraph",
        "label": "SEO Title",
        "placeholder": "Leave blank to use the post title"
      }
    },
    "seo_description": {
      "type": "StructuredText",
      "config": {
        "single": "paragraph",
        "label": "SEO Description"
      }
    },
    "seo_image": {
      "type": "Image",
      "config": {
        "label": "OG / Social Share Image",
        "constraint": {
          "width": 1200,
          "height": 630
        },
        "thumbnails": []
      }
    }
  }
}

Setting constraint: width/height on the image field tells Prismic to validate the dimensions — editors will see a warning if they upload the wrong size.

Step 2: Query and render OG metadata in Next.js

// app/blog/[uid]/page.tsx
import type { Metadata } from "next";
import { createClient } from "@/prismicio";
import { asText, asImageSrc } from "@prismicio/client";

export async function generateMetadata({
  params,
}: {
  params: { uid: string };
}): Promise<Metadata> {
  const client = createClient();
  const doc = await client.getByUID("blog_post", params.uid);
  
  if (!doc) return { title: "Not Found" };
  
  // Prefer explicit SEO fields; fall back to content fields
  const title = doc.data.seo_title
    ? asText(doc.data.seo_title)
    : asText(doc.data.title);
    
  const description = doc.data.seo_description
    ? asText(doc.data.seo_description)
    : doc.data.excerpt
    ? asText(doc.data.excerpt)
    : "";
  
  // Prefer SEO image; fall back to featured image
  const ogImage = doc.data.seo_image?.url || doc.data.featured_image?.url;
  
  return {
    title,
    description,
    openGraph: {
      title,
      description,
      type: "article",
      images: ogImage
        ? [
            {
              url: ogImage,
              width: doc.data.seo_image?.dimensions?.width || 1200,
              height: doc.data.seo_image?.dimensions?.height || 630,
            },
          ]
        : [],
    },
    twitter: {
      card: "summary_large_image",
      title,
      description,
      images: ogImage ? [ogImage] : [],
    },
  };
}

Using Prismic Image fields for OG images

Prismic Image fields include built-in responsive transforms. Use them to get exactly the right size for OG:

import { asImageSrc } from "@prismicio/client";

// Get URL with specific dimensions
const ogImageUrl = asImageSrc(doc.data.seo_image, {
  w: 1200,
  h: 630,
  fit: "crop",
  auto: "format",
  q: 85,
});

// Prismic Imgix URL format:
// https://images.prismic.io/repo/image.jpg?auto=format&fit=crop&w=1200&h=630

// In metadata:
images: ogImageUrl
  ? [{ url: ogImageUrl, width: 1200, height: 630 }]
  : [],

Slice-based pages and OG fallbacks

For Prismic slice-based pages (landing pages, etc.), there may be no obvious OG image in the content. Set up a fallback hierarchy:

function resolveOgImage(doc: PrismicDocument): string {
  // 1. Explicit SEO image field
  if (doc.data.seo_image?.url) {
    return asImageSrc(doc.data.seo_image, { w: 1200, h: 630, fit: "crop" })!;
  }
  
  // 2. First hero image found in slices
  for (const slice of doc.data.slices || []) {
    if (slice.variation === "default" && slice.primary?.background_image?.url) {
      return asImageSrc(slice.primary.background_image, {
        w: 1200,
        h: 630,
        fit: "crop",
      })!;
    }
  }
  
  // 3. Dynamic generated OG image
  const title = asText(doc.data.seo_title || doc.data.title);
  return `/api/og?title=${encodeURIComponent(title)}`;
}

Common Prismic OG pitfalls

  • asText() for titles: Prismic StructuredText fields return an array — always use asText() to extract plain string
  • Image URL expiry: Prismic image URLs via Imgix don't expire, but ensure no CDN rewrite strips transform params
  • Draft content: Prismic drafts are only accessible with preview tokens — ensure social scrapers get published content
  • Missing content-type: Always wrap generateMetadata in try/catch for unpublished UIDs

Preview your Prismic OG tags

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

Check your OG tags free →