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.

proptypedefaultrequireddescription
titlestring-YesMain Open Graph title rendered as og:title.
typestring | falseinferred from the type-driving object or websiteNoOverrides the inferred og:type value when set to a string, or omits the og:type tag entirely when set to false.
urlstring | URLderived from current request when Astro.site existsNoCanonical page URL rendered as og:url.
descriptionstring-NoDescription rendered as og:description.
siteNamestringintegration head.openGraph.siteNameNoSite label rendered as og:site_name.
localestring-NoOpen Graph locale value rendered as og:locale (for example en_US).
localeAlternatestring[][]NoAlternate locale values, each rendered as og:locale:alternate.
imageComponentProps<typeof OpenGraphImage>-NoStructured image metadata rendered as og:image tags.
audioComponentProps<typeof OpenGraphAudio>-NoStructured audio metadata rendered as og:audio tags.
videoComponentProps<typeof OpenGraphVideo>-NoStructured video metadata rendered as og:video tags.
articleComponentProps<typeof OpenGraphArticle>-NoType-driving article object; sets og:type=article.
bookComponentProps<typeof OpenGraphBook>-NoType-driving book object; sets og:type=book.
businessComponentProps<typeof OpenGraphBusiness>-NoBusiness namespace tags; keeps og:type=website.
musicComponentProps<typeof OpenGraphMusic>-NoType-driving music object; sets og:type=music.<subtype>.
placeComponentProps<typeof OpenGraphPlace>-NoPlace namespace tags; keeps og:type=website.
productComponentProps<typeof OpenGraphProduct>-NoProduct namespace tags; keeps og:type=website.
profileComponentProps<typeof OpenGraphProfile>-NoType-driving profile object; sets og:type=profile.
videoTypeComponentProps<typeof OpenGraphVideoType>-NoType-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} />}