Astro — SEO & JSON-LD
Every field you need for head tags and structured data is already on entry.data — the loader surfaces the full article. You emit the tags; Ghost Writr doesn’t inject anything into the page.
Head tags
Section titled “Head tags”Set the title, description, and canonical from entry.data. seoTitle falls back to title server-side, so it’s always a string; the rest can be null, so guard them.
---const { seoTitle, seoDescription, canonicalUrl, image } = p.data;const canonical = canonicalUrl ?? new URL(p.id, Astro.site).href;---<title>{seoTitle}</title><meta name="description" content={seoDescription ?? ""} /><link rel="canonical" href={canonical} /><meta property="og:title" content={seoTitle} />{seoDescription && <meta property="og:description" content={seoDescription} />}{image && <meta property="og:image" content={image.url} />}{image?.width && <meta property="og:image:width" content={String(image.width)} />}canonicalUrl is the article’s declared canonical (set when the content is syndicated from elsewhere). When it’s null, the page is its own canonical — fall back to the page URL, as above. This is your call; see SEO & JSON-LD.
JSON-LD
Section titled “JSON-LD”Build the graph in frontmatter and emit it with set:html (it’s trusted, generated server-side from your own data — not user input):
---const a = p.data;const url = a.canonicalUrl ?? new URL(p.id, Astro.site).href;
const author = a.author.kind === "real" ? { "@type": "Person", name: a.author.name, jobTitle: a.author.jobTitle ?? undefined, ...(a.author.sameAs.length ? { sameAs: a.author.sameAs } : {}), } : { "@type": "Organization", name: a.author.name };
const jsonLd = { "@context": "https://schema.org", "@type": a.formatType, // schema.org type hint, e.g. "Article" headline: a.title, description: a.seoDescription ?? undefined, inLanguage: a.inLanguage, datePublished: a.publishedAt.toISOString(), dateModified: a.updatedAt.toISOString(), mainEntityOfPage: url, ...(a.image ? { image: a.image.url } : {}), author,};---<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />author.kind picks the author type
Section titled “author.kind picks the author type”Fields at a glance
Section titled “Fields at a glance”formatType→ JSON-LD@type(schema.org hint, e.g."Article").inLanguage→ BCP-47inLanguageand anhtml lang.publishedAt/updatedAt→datePublished/dateModified(they’reDates — call.toISOString()).image→og:imageand JSON-LDimage(withwidth/height/srcset/altwhen present).canonicalUrl→<link rel="canonical">andmainEntityOfPage.
See Collection schema for the full entry.data shape and The article shape for the underlying contract.