Skip to content

Open Graph meta tags

When you share an article URL on Bluesky, X, Slack, or most social platforms, the platform fetches the page and reads its <meta> tags to generate a rich preview — a card with a title, description, and image. Without these tags, links render as plain text.

The Scribe SDK provides helpers that generate the correct Open Graph and Twitter Card tags from the article and site data you’ve already fetched.

For an article page, the helpers produce:

TagValue
<title>"{Article Title} — {Site Title}"
og:type"article"
og:titleArticle title
og:urlCanonical article URL
og:site_nameSite title
og:descriptionArticle description (when set)
og:imageSplash image URL (when set)
twitter:card"summary_large_image" if splash image, otherwise "summary"
twitter:titleArticle title
twitter:descriptionArticle description (when set)
twitter:imageSplash image URL (when set)

You provide a { title } override after the spread to keep your own title format in the browser tab while the OG tags get the article title undecorated.

Use articleMeta from @scribe-atp/react-router-framework. It returns MetaDescriptor[] for React Router’s meta export.

// app/routes/blog.$articleSlug.tsx
import { articleMeta } from '@scribe-atp/react-router-framework';
import { fetchArticleBySlug, fetchSite } from '@scribe-atp/core';
import type { Route } from './+types/Article';
export async function loader({ request, params }: Route.LoaderArgs) {
const [{ article, uri: documentUri }, site] = await Promise.all([
fetchArticleBySlug('alice.bsky.social', 'https://alice.bsky.social', params.articleSlug!, request.signal),
fetchSite('alice.bsky.social', 'https://alice.bsky.social', request.signal),
]);
return { article, documentUri, publicationUri: site.uri, site };
}
export function meta({ loaderData }: Route.MetaArgs) {
if (!loaderData) return [{ title: 'My Blog' }];
return [
...articleMeta(loaderData.article, loaderData.site),
{ title: `${loaderData.article.title} | My Blog` },
];
}

The og:url tag is set to the article’s canonical URL. buildCanonicalUrl from @scribe-atp/core derives this from article.canonicalUrl (if set by the author in Scribe CMS), or by combining the publication URL, site.urlPrefix, and article.path.

If your site uses a custom domain and the author’s canonical URL doesn’t match, set article.canonicalUrl in Scribe CMS to the correct URL.

Use the site variants for pages that list articles rather than displaying one:

import { siteMeta } from '@scribe-atp/react-router-framework';
export function meta({ loaderData }: Route.MetaArgs) {
if (!loaderData) return [{ title: 'My Blog' }];
return [
...siteMeta(loaderData.site),
{ title: 'My Blog' },
];
}