The product page
---import { getGeoffyProductMarkup } from "@geoffy/headless/astro";
const { lang, handle } = Astro.params;const geoffy = await getGeoffyProductMarkup( { siteKey: import.meta.env.GEOFFY_SITE_KEY }, handle, { canonicalUrl: `https://yourdomain.com/${lang}/products/${handle}`, locale: lang, },);---
<Layout> {geoffy && <Fragment set:html={geoffy.jsonLdScript} slot="head" />}
<YourProductUI handle={handle} />
{geoffy && <div data-geoffy-widget set:html={geoffy.widgetHtml} />}</Layout>The file path is an example; your route can be any shape, because you pass handle
explicitly.
What the helper returns
Section titled “What the helper returns”getGeoffyProductMarkup resolves to null, or to an object with two strings:
| Field | Contains | Where it goes |
|---|---|---|
jsonLdScript | a complete <script type="application/ld+json"> element | the <head>, via slot="head" |
widgetHtml | the widget markup, with its <style> inside it | wherever a shopper should see it |
Both are pre-rendered HTML strings, so both need set:html.
What handle must be
Section titled “What handle must be”| Your setup | handle is |
|---|---|
| Shopify behind your storefront | the Shopify product handle, the same value you already query the Storefront API with |
| No commerce platform | the segment your product URL pattern captured. Geoffy shows you the handle it recorded next to each crawled product |
See which kind of site is yours.
canonicalUrl is optional and worth passing
Section titled “canonicalUrl is optional and worth passing”Geoffy publishes content per page, against that page’s canonical URL. Pass
canonicalUrl so Geoffy answers for this page. Two pages may share a handle, such as
an untranslated slug in two locales or two categories that end in the same word.
This needs @geoffy/headless 0.3.5 or later.
Without canonicalUrl, Geoffy answers by handle alone with the page published last.
On another page with the same handle, that can be the wrong content.
If Geoffy has nothing published for this page, the helper returns null, so the
{geoffy && …} guards you already have render nothing.
If your site serves more than one language
Section titled “If your site serves more than one language”Pass locale and canonicalUrl. Each published locale renders its own content; a page
with nothing published renders nothing.
const geoffy = await getGeoffyProductMarkup( { siteKey: import.meta.env.GEOFFY_SITE_KEY }, handle, { canonicalUrl: canonical, locale: lang }, // lang: the locale this page renders, e.g. "sr");If the published content has a different language from the page, the helper declines without a product request to Geoffy when the published-page list is available. Only a clear language mismatch hides content; an unknown language does not.
Several locales under one Geoffy site work when each locale is its own page. Give
Geoffy a URL pattern per locale, then publish the pages you want. Each page gets content
in its own language. Pass that page’s own canonicalUrl and locale to select its
content and check its language.
Two locales may share a handle when you pass canonicalUrl. This needs
@geoffy/headless 0.3.5 or later. Older versions show a shared handle on one page only.
A worked example, with the same slug in two languages: several languages on one site.
It never breaks your page
Section titled “It never breaks your page”The helper resolves to null when Geoffy has nothing published, when Geoffy is
unreachable or erroring, and when locale or canonicalUrl does not match. It never throws.
A product with nothing published is a normal state, not an error, and a page for it makes no request to Geoffy. So you can call the helper on every product page, published or not.
Your storefront is your business: if we are having a bad minute, the correct behaviour is for your product page to render exactly as it would have without us. The fetch carries its own timeout for the same reason: a build should not hang because our origin is wedged.