@plugdash/socialcard

socialcard

470 words·3min read

What it does

On every publish, socialcard reads the post's title, author, and publish date, renders one of three SVG card templates, uploads it, and writes the URL to post.data.metadata.ogImage. Your theme points og:image at that URL. No design tool, no third-party image service.

It only runs for content with status published. Drafts, archived, and scheduled content are skipped. A rendering or upload error is logged, never thrown - a bad card is never a reason to fail a publish.

Install

npm install @plugdash/socialcard

Register

Open astro.config.mjs at the root of your Astro project. Import the plugin at the top, then add it to the pluginsarray of the emdash() integration - the one that sits inside Astro's top-level integrations array:

astro.config.mjsjs
import { defineConfig } from "astro/config"
import emdash from "emdash/astro"
import { socialcardPlugin } from "@plugdash/socialcard"

export default defineConfig({
  integrations: [
    emdash({
      plugins: [
        socialcardPlugin({
          template: "bold",
          background: "#111827",
          foreground: "#fef3c7",
        }),
      ],
    }),
  ],
})

Configuration

Pass options at register time. They are seeded into the plugin's KV store on install, and reseeded automatically whenever the config object in astro.config.mjs changes - so editing the file and restarting is enough, you don't need to touch the admin.

Props

proptypedefaultdescription
template"default" | "minimal" | "bold""default"Card layout
widthnumber1200Card width in pixels
heightnumber630Card height in pixels
backgroundstring (hex)"#0f172a"Background colour
foregroundstring (hex)"#f8fafc"Text (and, on minimal, paper) colour
logostring (URL)Logo drawn in the top-left corner
fonts.titlestring (CSS font stack)system sansFont stack for the title line
fonts.bodystring (CSS font stack)system sansFont stack for the byline

Templates

There are three built-in layouts:

  • default - large title, small author + date byline, subtle gradient over the background colour
  • minimal - title only, on a clean paper background (the palette is inverted from the other two, so the defaults read as dark text on light paper)
  • bold - large type set against a strong colour block down the left edge

Here's the same post rendered with each one:

Share card rendered with the default template
default
Share card rendered with the minimal template
minimal
Share card rendered with the bold template
bold

Titles over 80 characters are truncated with an ellipsis. The byline is author and date joined by a middle dot - if either is missing, that half is dropped; if both are missing, the byline line is omitted entirely rather than left blank.

Using the card in your layout

Read metadata.ogImage from the post and point your meta tags at it:

astro
---
const post = await emdash.content.get("posts", Astro.params.id)
const { ogImage } = post.data.metadata ?? {}
---

{ogImage && <meta property="og:image" content={ogImage} />}
{ogImage && <meta property="og:image:width" content="1200" />}
{ogImage && <meta property="og:image:height" content="630" />}
{ogImage && <meta name="twitter:image" content={ogImage} />}

One real limitation worth knowing before you rely on this: the card is an image/svg+xml file, not a PNG. Most OG consumers accept SVG fine, but Twitter/X and Facebook specifically rejectimage/svg+xml for og:image andtwitter:image. Treat this as an interim format until a rasterised version ships - see the package README for why (a sandboxed plugin bundle has no way to load the font binary or wasm blob that a PNG renderer needs).

No component

socialcard has no companion Astro component. It is pure infrastructure - it writes a URL to metadata and gets out of the way. Wiring that URL into your <head> is a couple of meta tags in your own layout, shown above.

For agents

After installing and registering the plugin, publish a test post and confirm post.data.metadata.ogImage is populated with a URL ending in .svg. If it is not:

  • Check the post status is "published", not draft
  • Confirm the plugin declares content:read, content:write, and media:write
  • Republish - existing posts saved before the plugin was installed need a new save to pick it up
built with plugdash.dev