Skip to content

OpenGraph

OpenGraph renders a complete set of Open Graph meta tags with a type-safe API. It infers the primary og:type from a single type-driving object, lets you override that value explicitly, and outputs media and namespace tags based on the props you provide.

prop type default required description
title string - Yes Main Open Graph title rendered as og:title.
type string | false inferred from the type-driving object or website No Overrides the inferred og:type value when set to a string, or omits the og:type tag entirely when set to false.
url string | URL derived from current request when Astro.site exists No Canonical page URL rendered as og:url.
description string - No Description rendered as og:description.
siteName string integration head.openGraph.siteName No Site label rendered as og:site_name.
locale string - No Open Graph locale value rendered as og:locale (for example en_US).
localeAlternate string[] [] No Alternate locale values, each rendered as og:locale:alternate.
image ComponentProps<typeof OpenGraphImage> - No Structured image metadata rendered as og:image tags.
audio ComponentProps<typeof OpenGraphAudio> - No Structured audio metadata rendered as og:audio tags.
video ComponentProps<typeof OpenGraphVideo> - No Structured video metadata rendered as og:video tags.
article ComponentProps<typeof OpenGraphArticle> - No Type-driving article object; sets og:type=article.
book ComponentProps<typeof OpenGraphBook> - No Type-driving book object; sets og:type=book.
business ComponentProps<typeof OpenGraphBusiness> - No Business namespace tags; keeps og:type=website.
music ComponentProps<typeof OpenGraphMusic> - No Type-driving music object; sets og:type=music.<subtype>.
place ComponentProps<typeof OpenGraphPlace> - No Place namespace tags; keeps og:type=website.
product ComponentProps<typeof OpenGraphProduct> - No Product namespace tags; keeps og:type=website.
profile ComponentProps<typeof OpenGraphProfile> - No Type-driving profile object; sets og:type=profile.
videoType ComponentProps<typeof OpenGraphVideoType> - No Type-driving video object; sets og:type=video.<subtype>.

Input:

<OpenGraph title="Home" url="https://example.com/" siteName="Example" />

Output:

<meta property="og:type" content="website" />
<meta property="og:title" content="Home" />
<meta property="og:url" content="https://example.com/" />
<meta property="og:site_name" content="Example" />

Automatically derives og:url from the current request when Astro.site is configured.

Input:

<OpenGraph title="Home" />

Output (when site context is available):

<meta property="og:type" content="website" />
<meta property="og:title" content="Home" />
<meta property="og:url" content="https://example.com/current/path" />

Input:

<OpenGraph
title="My Article"
url="https://example.com/posts/1"
description="Open Graph overview"
siteName="Example"
locale="en_US"
localeAlternate={["es_ES", "fr_FR"]}
image={{
src: "https://example.com/og.png",
width: 1200,
height: 630,
alt: "Banner",
}}
article={{
publishedTime: "2026-01-01T00:00:00Z",
authors: ["https://example.com/author"],
section: "Tech",
tags: ["astro", "seo"],
}}
/>

Output:

<meta property="og:type" content="article" />
<meta property="og:title" content="My Article" />
<meta property="og:url" content="https://example.com/posts/1" />
<meta property="og:description" content="Open Graph overview" />
<meta property="og:site_name" content="Example" />
<meta property="og:locale" content="en_US" />
<meta property="og:locale:alternate" content="es_ES" />
<meta property="og:locale:alternate" content="fr_FR" />
<meta property="og:image" content="https://example.com/og.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Banner" />
<meta property="article:published_time" content="2026-01-01T00:00:00Z" />
<meta property="article:author" content="https://example.com/author" />
<meta property="article:section" content="Tech" />
<meta property="article:tag" content="astro" />
<meta property="article:tag" content="seo" />

When type is provided as a string, OpenGraph uses that value instead of the inferred one. For example, <OpenGraph title="Home" type="article" /> renders og:type=article even without an article object, and <OpenGraph title="Home" type={false} /> omits the og:type tag entirely.

When type is omitted, the component picks og:type from the type-driving object in this canonical order: article, book, profile, music, videoType, and then website fallback. This keeps output predictable and avoids ambiguous type combinations.

Non-canonical namespaces keep website as og:type

Section titled “Non-canonical namespaces keep website as og:type”

Business, place, and product render their namespace-specific tags while og:type remains website. This follows the component’s explicit behavior for OG namespaces that do not map to a canonical top-level og:type value.

When url is omitted, the component builds it from the current path, query, and hash using Astro.site as the base. This removes repetitive per-page URL wiring in common setups.

Locale values are passed through as provided

Section titled “Locale values are passed through as provided”

The component does not normalize locale formats. You should provide Open Graph locale values such as en_US and es_ES directly.

---
import type { ComponentProps } from "astro/types";
import { toHref } from "../utils";
import OpenGraphArticle from "./openGraph/OpenGraphArticle.astro";
import OpenGraphAudio from "./openGraph/OpenGraphAudio.astro";
import OpenGraphBook from "./openGraph/OpenGraphBook.astro";
import OpenGraphBusiness from "./openGraph/OpenGraphBusiness.astro";
import OpenGraphImage from "./openGraph/OpenGraphImage.astro";
import OpenGraphMusic from "./openGraph/OpenGraphMusic.astro";
import OpenGraphPlace from "./openGraph/OpenGraphPlace.astro";
import OpenGraphProduct from "./openGraph/OpenGraphProduct.astro";
import OpenGraphProfile from "./openGraph/OpenGraphProfile.astro";
import OpenGraphVideo from "./openGraph/OpenGraphVideo.astro";
import OpenGraphVideoType from "./openGraph/OpenGraphVideoType.astro";
import config from "virtual:eminence-astro-suite/head-tags";
type BaseProps = {
title: string;
/**
* Overrides inferred `og:type`; set to `false` to omit the tag entirely.
*/
type?: string | false;
/**
* Defaults to the current page URL when `Astro.site` is configured.
*/
url?: string | URL;
description?: string;
/**
* Defaults to the integration-level `head.openGraph.siteName` value.
*/
siteName?: string;
/**
* Passed through as provided; use Open Graph format values like `en_US`.
*/
locale?: string;
/**
* Each entry emits an `og:locale:alternate` tag in Open Graph format.
*/
localeAlternate?: string[];
/**
* Optional rich preview image metadata for social cards.
*/
image?: ComponentProps<typeof OpenGraphImage>;
audio?: ComponentProps<typeof OpenGraphAudio>;
video?: ComponentProps<typeof OpenGraphVideo>;
};
type TypedVariant =
| {
article: ComponentProps<typeof OpenGraphArticle>;
book?: never;
business?: never;
music?: never;
place?: never;
product?: never;
profile?: never;
videoType?: never;
}
| {
article?: never;
book: ComponentProps<typeof OpenGraphBook>;
business?: never;
music?: never;
place?: never;
product?: never;
profile?: never;
videoType?: never;
}
| {
article?: never;
book?: never;
business: ComponentProps<typeof OpenGraphBusiness>;
music?: never;
place?: never;
product?: never;
profile?: never;
videoType?: never;
}
| {
article?: never;
book?: never;
business?: never;
music: ComponentProps<typeof OpenGraphMusic>;
place?: never;
product?: never;
profile?: never;
videoType?: never;
}
| {
article?: never;
book?: never;
business?: never;
music?: never;
place: ComponentProps<typeof OpenGraphPlace>;
product?: never;
profile?: never;
videoType?: never;
}
| {
article?: never;
book?: never;
business?: never;
music?: never;
place?: never;
product: ComponentProps<typeof OpenGraphProduct>;
profile?: never;
videoType?: never;
}
| {
article?: never;
book?: never;
business?: never;
music?: never;
place?: never;
product?: never;
profile: ComponentProps<typeof OpenGraphProfile>;
videoType?: never;
}
| {
article?: never;
book?: never;
business?: never;
music?: never;
place?: never;
product?: never;
profile?: never;
videoType?: ComponentProps<typeof OpenGraphVideoType>;
}
| {
article?: never;
book?: never;
business?: never;
music?: never;
place?: never;
product?: never;
profile?: never;
videoType?: never;
};
type Props = BaseProps & TypedVariant;
const {
title,
type,
url,
description,
siteName = config.openGraphSiteName,
image,
audio,
video,
locale,
localeAlternate = [],
article,
book,
business,
music,
place,
product,
profile,
videoType,
} = Astro.props;
const inferredUrl = Astro.site
? new URL(Astro.url.pathname + Astro.url.search + Astro.url.hash, Astro.site)
.href
: undefined;
const resolvedUrl = url ? toHref(url) : inferredUrl;
const inferredType = (() => {
// Canonical OG types take precedence; non-canonical namespaces keep website.
if (article) return "article";
if (book) return "book";
if (profile) return "profile";
if (music) return `music.${music.subtype}`;
if (videoType) return `video.${videoType.subtype}`;
return "website";
})();
const resolvedType = type === undefined ? inferredType : type;
---
{resolvedType !== false && <meta property="og:type" content={resolvedType} />}
{title && <meta property="og:title" content={title} />}
{resolvedUrl && <meta property="og:url" content={resolvedUrl} />}
{description && <meta property="og:description" content={description} />}
{siteName && <meta property="og:site_name" content={siteName} />}
{locale && <meta property="og:locale" content={locale} />}
{
localeAlternate.map((locale) => (
<meta property="og:locale:alternate" content={locale} />
))
}
{image && <OpenGraphImage {...image} />}
{audio && <OpenGraphAudio {...audio} />}
{video && <OpenGraphVideo {...video} />}
{article && <OpenGraphArticle {...article} />}
{book && <OpenGraphBook {...book} />}
{business && <OpenGraphBusiness {...business} />}
{music && <OpenGraphMusic {...music} />}
{place && <OpenGraphPlace {...place} />}
{product && <OpenGraphProduct {...product} />}
{profile && <OpenGraphProfile {...profile} />}
{videoType && <OpenGraphVideoType {...videoType} />}