<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>OpenGraph | Nerdpress.org</title>
	<atom:link href="https://nerdpress.org/tag/opengraph/feed/" rel="self" type="application/rss+xml" />
	<link>https://nerdpress.org</link>
	<description>...dev, tech problems and solutions.</description>
	<lastBuildDate>Wed, 24 Dec 2025 12:41:32 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Dynamic OpenGraph Images in Astro</title>
		<link>https://nerdpress.org/2025/12/23/dynamic-opengraph-images-in-astro/</link>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Tue, 23 Dec 2025 13:13:06 +0000</pubDate>
				<category><![CDATA[Astro]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[OpenGraph]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=3457</guid>

					<description><![CDATA[<p>If you&#8217;ve ever shared a link on social media, you know how critical OpenGraph (OG) images are. They&#8217;re the first thing people see &#8211; often before they even click.Static OG images are fine as a start, but what if you want custom images for every blog post or content collection item? For Astro there is &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2025/12/23/dynamic-opengraph-images-in-astro/" class="more-link">Continue reading<span class="screen-reader-text"> "Dynamic OpenGraph Images in Astro"</span></a></p>
The post <a href="https://nerdpress.org/2025/12/23/dynamic-opengraph-images-in-astro/">Dynamic OpenGraph Images in Astro</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>If you&#8217;ve ever shared a link on social media, you know how critical OpenGraph (OG) images are. They&#8217;re the first thing people see &#8211; often before they even click.<br />Static OG images are fine as a start, but what if you want <strong>custom images for every blog post or content collection item</strong>?</p>



<p>For Astro there is <a href="https://github.com/delucis/astro-og-canvas" target="_blank" rel="noopener" title="">astro-og-canvas</a>, a nice and useful Astro plugin that utilizes Canvas to create dynamic OG images.</p>



<p>In this post, I&#8217;ll walk you through how to generate dynamic OG images for your Astro site, inspired by <a href="https://aidankinzett.com/blog/astro-open-graph-image/" target="_blank" rel="noopener" title="">Aidan Kinzett&#8217;s excellent post</a>.<br />I&#8217;ll also share some odds and learned lessons.</p>



<span id="more-3457"></span>



<p><strong>Step 1: Set Up `astro-og-canvas`</strong></p>



<p>First, install the package:</p>



<p><code>npm install astro-og-canvas</code></p>



<p>Create a new file at <code>src/pages/og/[...routes].ts</code>.<br />This will dynamically generate images for your content.<br />Here&#8217;s how I set it up for my newsletter collection:</p>


<pre class="wp-block-code"><span><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { OGImageRoute } <span class="hljs-keyword">from</span> <span class="hljs-string">"astro-og-canvas"</span>;
<span class="hljs-keyword">import</span> { getCollection } <span class="hljs-keyword">from</span> <span class="hljs-string">"astro:content"</span>;

<span class="hljs-comment">// Fetch all newsletter entries</span>
<span class="hljs-keyword">const</span> newsletters = <span class="hljs-keyword">await</span> getCollection(<span class="hljs-string">"newsletter"</span>);

<span class="hljs-comment">// Map newsletters to OG image configurations</span>
<span class="hljs-keyword">const</span> pages = {
  ...Object.fromEntries(
    newsletters.map(<span class="hljs-function">(<span class="hljs-params"><span class="hljs-params">newsletter</span></span>) =&gt;</span> &#91;
      newsletter.data.slug,
      {
        data: {
          title: newsletter.data.title,
          description: newsletter.data.newsletter.parts
            .map(<span class="hljs-function">(<span class="hljs-params"><span class="hljs-params">part</span></span>) =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${part.emoji.icon}</span> <span class="hljs-subst">${part.title}</span>`</span>)
            .join(<span class="hljs-string">" • "</span>),
        },
        slug: newsletter.data.slug,
      },
    ])
  ),
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { getStaticPaths, GET } = OGImageRoute({
  param: <span class="hljs-string">"route"</span>,
  pages,
  getImageOptions: <span class="hljs-keyword">async</span> (_, { data, slug }) =&gt; ({
    title: <span class="hljs-string">`Ivo's Ecotainment Newsletter: <span class="hljs-subst">${data.title}</span>`</span>,
    description: data.description,
    bgGradient: &#91;
      &#91;<span class="hljs-number">255</span>, <span class="hljs-number">221</span>, <span class="hljs-number">0</span>], <span class="hljs-comment">// Yellow (from logo)</span>
      &#91;<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>], <span class="hljs-comment">// White</span>
    ],
    logo: {
      path: <span class="hljs-string">"./public/Ivos-Ecotainment-Newsletter_Salamander.png"</span>,
      size: &#91;<span class="hljs-number">1080</span>],
    },
    border: {
      color: &#91;<span class="hljs-number">83</span>, <span class="hljs-number">174</span>, <span class="hljs-number">90</span>], <span class="hljs-comment">// Green accent</span>
      width: <span class="hljs-number">2</span>,
      side: <span class="hljs-string">"inline-start"</span>,
    },
    font: {
      title: {
        size: <span class="hljs-number">42</span>,
        weight: <span class="hljs-string">"Bold"</span>,
        families: &#91;<span class="hljs-string">"Noto Sans"</span>, <span class="hljs-string">"Noto Color Emoji"</span>],
        color: &#91;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>],
      },
      description: {
        size: <span class="hljs-number">20</span>,
        weight: <span class="hljs-string">"Normal"</span>,
        families: &#91;<span class="hljs-string">"Noto Sans"</span>, <span class="hljs-string">"Noto Color Emoji"</span>],
        color: &#91;<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>],
        lineHeight: <span class="hljs-number">1.4</span>,
      },
    },
    fonts: &#91;
      <span class="hljs-string">"./public/fonts/Noto_Sans/NotoSans-VariableFont_wdth,wght.ttf"</span>,
      <span class="hljs-string">"./public/fonts/Noto_Color_Emoji/NotoColorEmoji-Regular.ttf"</span>,
    ],
    padding: <span class="hljs-number">50</span>,
  }),
});</code></span></pre>


<p><br /><strong>Some learned lessons:</strong><br />1. Logos: SVGs won&#8217;t work here. Use PNGs instead.<br />2. Fonts: Add font files, if you use fonts.<br />3. Emojis: If you use emojis in your content, include an emoji font (like &#8220;Noto Color Emoji&#8221;) to ensure they render correctly.</p>



<p><strong>Step 2: Integrate with astro-seo</strong></p>



<p>I use the <a href="https://github.com/jonasmerlin/astro-seo" target="_blank" rel="noopener" title="">astro-seo</a> component in my main Layout component to handle SEO metadata.</p>



<p>For the OG image I add a prop to be passed from the respective page:</p>


<pre class="wp-block-code"><span><code class="hljs language-typescript"><span class="hljs-keyword">const</span> { title, description, ogImage } = Astro.props;

&lt;SEO
  title={title}
  description={description}
  openGraph={{
    basic: {
      title,
      <span class="hljs-keyword">type</span>: <span class="hljs-string">"website"</span>,
      image: ogImage || <span class="hljs-string">`<span class="hljs-subst">${Astro.site}</span>Ivos-Ecotainment-Newsletter_big.png`</span>, <span class="hljs-comment">// Fallback</span>
    },
  }}
/&gt;</code></span></pre>


<p><strong>In Your Content Pages</strong>:<br />Construct the OG image path and pass it to your layout:</p>


<pre class="wp-block-code"><span><code class="hljs language-typescript"><span class="hljs-keyword">const</span> ogImage = <span class="hljs-string">`<span class="hljs-subst">${Astro.site?.href || <span class="hljs-string">""</span>}</span>og/<span class="hljs-subst">${entry.data.slug}</span>.png`</span>;

&lt;Layout title={entry.data.title} description={description} ogImage={ogImage}&gt;
  {<span class="hljs-comment">/* Post content */</span>}
&lt;<span class="hljs-regexp">/Layout&gt;</span></code></span></pre>


<p><strong>Astro Odds</strong>:</p>



<p>A bit odd is if your <code>astro.config.mjs</code> has <code>trailingSlash: "always"</code>, you&#8217;ll need to add a trailing slash to OG image URLs <strong>in development</strong>:<br /><code>http://localhost:4321/og/${entry.data.slug}.png/</code></p>



<p>In prod (no trailing slash needed) so you can just pass it as: <code>http://localhost:4321/og/${entry.data.slug}.png</code></p>



<p></p>



<p><strong>3. Testing OG Images</strong></p>



<p>As recommend in the Blog post of Aidan Kinzett: Use <a href="https://www.opengraph.xyz" target="_blank" rel="noopener" title="">opengraph.xyz</a>  to preview your OG images before sharing links.<br />Very good tool!</p>



<p>Here is how the OG image looks like with logo, title and description with emojis:</p>



<figure class="wp-block-image size-large"><a href="https://ivos-ecotainment-newsletter.info/archiv/pt58-party-parrot/"><img fetchpriority="high" decoding="async" width="1024" height="538" src="https://nerdpress.org/wp-content/uploads/2025/12/pt58-party-parrot-1024x538.png" alt="" class="wp-image-3462" srcset="https://nerdpress.org/wp-content/uploads/2025/12/pt58-party-parrot-1024x538.png 1024w, https://nerdpress.org/wp-content/uploads/2025/12/pt58-party-parrot-300x158.png 300w, https://nerdpress.org/wp-content/uploads/2025/12/pt58-party-parrot-768x403.png 768w, https://nerdpress.org/wp-content/uploads/2025/12/pt58-party-parrot.png 1200w" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" /></a></figure>



<p>Happy hacking!</p>



<p></p>The post <a href="https://nerdpress.org/2025/12/23/dynamic-opengraph-images-in-astro/">Dynamic OpenGraph Images in Astro</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Opengraph MetaTags in SilverStripe</title>
		<link>https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/</link>
					<comments>https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/#comments</comments>
		
		<dc:creator><![CDATA[Ivo Bathke]]></dc:creator>
		<pubDate>Thu, 14 Jun 2012 15:33:41 +0000</pubDate>
				<category><![CDATA[Silverstripe]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[OpenGraph]]></category>
		<guid isPermaLink="false">https://nerdpress.org/?p=2208</guid>

					<description><![CDATA[<p>First off some general thoughts on how to use opengraph metatags. Since the uprise of Social Media, sharing sites, deeplinking and snippetting content has become a important aspect of SEO and so almost every site has some kind of facebook-like-button to let user easily share the page. Most sharing endpoints, facebook f.e, bring a more &#8230; </p>
<p class="link-more"><a href="https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/" class="more-link">Continue reading<span class="screen-reader-text"> "Opengraph MetaTags in SilverStripe"</span></a></p>
The post <a href="https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/">Opengraph MetaTags in SilverStripe</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></description>
										<content:encoded><![CDATA[<p>First off some general thoughts on how to use opengraph metatags.<br />
Since the uprise of Social Media, sharing sites, deeplinking and snippetting content has become a important aspect of SEO and so almost every site has some kind of facebook-like-button to let user easily share the page.<br />
Most sharing endpoints, facebook f.e, bring a more or less good parser/linter to summarize the page content. So the first text paragraph will be taken as text snippet and all images that fit certain requirements will be offered to chose a preview-thumbnail from.<br />
So far so good.<br />
<span id="more-2208"></span><br />
But sometimes you want to have control of the content that is used for the preview on the sharer site.<br />
And thats where the opengraph metatags come into play. With them you can specify title, description and image of the preview snippet.</p>
<p>Thats great, but it has some caveats, which i like to describe while introducing the silverstripe <a href="https://github.com/tractorcow/silverstripe-opengraph">opengraph module</a> by Damian Mooyman.</p>
<p>The module is pretty well done and delivers most content prefilled. So just install it and the rest comes automatically through the <em>$MetaTags</em> var in your template.<br />
In the backend you can insert some information like locality and your facebook app id.<br />

<a href='https://nerdpress.org/wp-content/uploads/2012/06/facebook-opengraph-silverstripe.png'><img decoding="async" width="150" height="150" src="https://nerdpress.org/wp-content/uploads/2012/06/facebook-opengraph-silverstripe-150x150.png" class="attachment-thumbnail size-thumbnail" alt="" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" /></a>
<a href='https://nerdpress.org/wp-content/uploads/2012/06/opengraph-module-silverstripe.png'><img decoding="async" width="150" height="150" src="https://nerdpress.org/wp-content/uploads/2012/06/opengraph-module-silverstripe-150x150.png" class="attachment-thumbnail size-thumbnail" alt="" sizes="(max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px" /></a>
</p>
<p>You should further add a default 200x200px image in your config:</p>
<pre class="brush: php; title: ; notranslate">OpenGraphPageExtension::$default_image = '/themes/myTheme/images/og_image.png';</pre>
<p>and add the opengraph namespace var in your html tag:</p>
<pre class="brush: xml; title: ; notranslate">&lt;html lang=&quot;en&quot; $OGNS&gt;</pre>
<p>Thats basically it, wow!</p>
<p>But of course you have to do some finetuning to represent your content best.<br />
So these questions you should ask yourself for each site where you would like to implement the metatags:</p>
<p><strong>Why should you use opengraph metatags?</strong><br />
To have control over the content of the sharer snippets.</p>
<p><strong>Where should you use opengraph metatags?</strong><br />
Where you can control / predict the content.<br />
Its not always a good idea to preselect the image.<br />
F.E when you have RichText HTML Field where the Content Manager can insert images. Then those images might be good to be shared.<br />
But with a preselected image via the og:image tag, the facebook share box will <strong>not</strong> list the other images.<br />
So on some pagetypes it might be better to not use the og tags and let the facebook parser do its <del datetime="2012-06-14T15:02:22+00:00">dirty</del> work.<br />
This you can achieve with overwriting the getOGType function in your pagetype:</p>
<pre class="brush: php; title: ; notranslate">
NonOGPage extends Page
{
    function getOGType() {
        return null;
    }
}
</pre>
<p>(thanx <a href="https://github.com/tractorcow">Damian</a>)</p>
<p><strong>How should you use it?</strong><br />
Treat your content right. So if you have a structured page type, think of which content fits and overwrite the resp. function of the module to serve the content you would like to see.<br />
F.E. if you have multiple images and you want the user to choose from while in the share dialog then do something like that:</p>
<pre class="brush: php; title: ; notranslate">
PageWithImages extends Page {
     function OGImage() {
        $imgs = array();
        if ($this-&gt;Images()) {
            $images = $this-&gt;Images();

            foreach ($images as $value) {
                $og_img = $value-&gt;Thumb()-&gt;CroppedImage(200, 200);
                $imgs&#x5B;] = $og_img-&gt;Url;
            }
        }
        if(empty($imgs)){
            $imgs&#x5B;] = Director::absoluteURL(OpenGraphPageExtension::$default_image);
        }
        return $imgs;
    }
}
</pre>
<p><strong>Who uses the opengraph metatags?</strong><br />
&#8211; Facebook of course.<br />
&#8211; Google+ seems	to prefer schema.org annotation but uses opengraph as fallback and then meta tags and then content.<br />
&#8211; Pinterest also seems to use it, partly.<br />
Here is a interesting discussion about it:<br />
<a href="http://stackoverflow.com/questions/6536213/are-there-tags-to-specify-the-google-1-story-format-in-google-like-og-meta-for">http://stackoverflow.com/questions/6536213/are-there-tags-to-specify-the-google-1-story-format-in-google-like-og-meta-for</a><br />
&#8211; and probably some more&#8230;</p>
<p>And at last here is the facebook <a href="http://developers.facebook.com/tools/debug">debug tool</a> to check your opengraph contents.</p>The post <a href="https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/">Opengraph MetaTags in SilverStripe</a> first appeared on <a href="https://nerdpress.org">Nerdpress.org</a>.]]></content:encoded>
					
					<wfw:commentRss>https://nerdpress.org/2012/06/14/opengraph-metatags-in-silverstripe/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
