SEO meta (ArticleHead)
@ghostwritr/svelte ships SEO two ways. The <ArticleHead> component renders an Article’s <title>, Open Graph + Twitter card, the canonical <link>, and schema.org Article JSON-LD straight into <svelte:head>. Under it sits articleHead(article, opts?) — a pure function that returns plain head data so you can build your own <svelte:head>. Both are driven by the feed’s guarantees. See SEO & JSON-LD for the cross-SDK model.
Both live at the core entry:
import { ArticleHead, articleHead } from "@ghostwritr/svelte";import type { ArticleHeadData, ArticleHeadOptions } from "@ghostwritr/svelte";The component
Section titled “The component”Drop <ArticleHead> into a page. It renders into <svelte:head> itself, so there’s nothing else to wire — no head manager, no useHead:
<script lang="ts"> import { ArticleHead } from "@ghostwritr/svelte"; let { data } = $props();</script>
<!-- <title>, OG/Twitter, canonical, and schema.org Article JSON-LD into <svelte:head>. --><ArticleHead article={data.article} siteName="Acme" twitterSite="@acme" />
<article> <h1>{data.article.title}</h1></article><ArticleHead> takes the article plus the three ArticleHeadOptions fields as props:
| Prop | Type | Effect |
|---|---|---|
article | Article | The article to describe (required). |
url | string | Absolute URL of this page → <link rel="canonical"> + og:url. Falls back to article.canonicalUrl. |
siteName | string | → og:site_name and the JSON-LD publisher (an Organization). |
twitterSite | string | → twitter:site (your default @handle). |
The pure function
Section titled “The pure function”When you want a custom <svelte:head> — to merge in a robots tag, reorder, or drop a field — call articleHead(article, opts?) and render the pieces yourself. It returns an ArticleHeadData: a plain object that never touches the document, so it’s SSR-safe and side-effect-free.
<script lang="ts"> import { articleHead } from "@ghostwritr/svelte"; let { data } = $props(); const head = $derived(articleHead(data.article, { siteName: "Acme" }));</script>
<svelte:head> {#if head.title}<title>{head.title}</title>{/if} {#each head.meta ?? [] as m} <meta name={m.name} property={m.property} content={m.content} /> {/each} {#each head.link ?? [] as l} <link rel={l.rel} href={l.href} /> {/each} {#each head.script ?? [] as s} {@html `<script type="${s.type}">${s.innerHTML}<\/script>`} {/each} <meta name="robots" content="index,follow" /></svelte:head>ArticleHeadData is the shape <ArticleHead> renders for you:
| Field | Type | Notes |
|---|---|---|
title | string (optional) | The <title> — article.seoTitle. |
meta | Array<{ name?, property?, content }> | OG, Twitter, and article:* tags. |
link | Array<{ rel: "canonical", href }> | Present only when a canonical resolves. |
script | Array<{ type: "application/ld+json", innerHTML }> | The Article JSON-LD. innerHTML is pre-serialized with < escaped, so a stray </script> in a field can’t break out of the element. |
Options
Section titled “Options”ArticleHeadOptions is optional — and so is every field within it.
| Option | Type | Effect |
|---|---|---|
url | string | Absolute URL of this page. Sets <link rel="canonical"> and og:url. Falls back to article.canonicalUrl. Pass your own when self-hosting under a different domain. |
siteName | string | Sets og:site_name and becomes the JSON-LD publisher (an Organization). |
twitterSite | string | Sets twitter:site (your default @handle). |
What it emits
Section titled “What it emits”From a typical article, articleHead returns an ArticleHeadData with title, a meta array, an optional canonical link, and the JSON-LD script:
title=article.seoTitle—seoTitlealways falls back totitleupstream, so it’s never empty.{ name: "description", content: article.seoDescription }— only whenseoDescriptionis set.og:type="article",og:title=seoTitle, andog:description(when present).og:url(when a canonical resolves),og:site_name(whensiteNameis given),og:image(when the article has animage).article:published_time=publishedAt,article:modified_time=updatedAt, and onearticle:tagper entry intags.twitter:card="summary_large_image"when the article has an image, otherwise"summary"; plustwitter:sitewhentwitterSiteis given.link=[{ rel: "canonical", href }]— only when a canonical resolves.script=[{ type: "application/ld+json", innerHTML }]— the schema.orgArticleJSON-LD (below).
The JSON-LD
Section titled “The JSON-LD”The injected Article JSON-LD carries headline (= article.title), description (when set), url + mainEntityOfPage (when a canonical resolves), image (when present), datePublished, dateModified, inLanguage, the author, and publisher (when siteName is given).
The author shape follows author.kind:
"real"→ aPersonwithname, plusjobTitleandsameAswhen present."persona"→ anOrganizationbyline (justname). Ghost Writr never fabricates a human for a brand voice — see SEO & JSON-LD.