No commerce platform
Your products live in an Astro content collection, a CMS, or a database. There is no commerce platform to sync from, so Geoffy builds its catalogue by reading your own pages.
The code on your product page is identical to the Shopify case. What differs is where
handle comes from and what you do in the dashboard first.
Step one is in the dashboard, not in your editor
Section titled “Step one is in the dashboard, not in your editor”-
Geoffy reads your sitemap and proposes URL patterns that look like product pages. You need a working
sitemap.xmlfirst —@astrojs/sitemapis the usual answer. -
You confirm the pattern, for example
/products/:handle. The segment it captures becomes each product’s handle. -
Geoffy crawls the matching pages and records what it could read about each product.
-
You review what it found — per product, including the canonical URL each page declares.
-
You publish the products worth publishing.
Make your pages readable first
Section titled “Make your pages readable first”The crawl reads what your HTML says, so what Geoffy has to work with is decided by your product page:
| Give it | Why |
|---|---|
A real <h1> with the product name | The strongest signal of what the page is about |
A <link rel="canonical"> naming this product | Without it the product is unpublishable — canonical_broken |
| Product facts as text, not only in images | A specification table in a JPEG is invisible |
Your own Product structured data, if you have it | Geoffy supplements it rather than duplicating it |
The product page
Section titled “The product page”---import { getCollection, getEntry } from "astro:content";import Layout from "@/layouts/Layout.astro";import { getGeoffyProductMarkup } from "@geoffy/headless/astro";
export const prerender = false;
const { handle } = Astro.params;const product = await getEntry("products", handle!);if (!product) return Astro.redirect("/404");
const { Content } = await product.render();const canonical = `https://yourdomain.com/products/${handle}`;
const geoffy = await getGeoffyProductMarkup( { siteKey: import.meta.env.GEOFFY_SITE_KEY }, handle, { canonicalUrl: canonical },);---
<Layout title={product.data.name}> <link rel="canonical" href={canonical} slot="head" /> {geoffy && <Fragment set:html={geoffy.jsonLdScript} slot="head" />}
<article> <h1>{product.data.name}</h1> <Content />
{geoffy && <div data-geoffy-widget set:html={geoffy.widgetHtml} />} </article></Layout>Prerendering this page is fine, with one caveat
Section titled “Prerendering this page is fine, with one caveat”Unlike a Shopify-backed catalogue, your prices and stock do not move underneath you, so prerendering the product page is reasonable. The caveat is that Geoffy’s markup is then baked in at build time: publishing in Geoffy changes nothing until you rebuild.
prerender is per route, so you can prerender products and still serve the namespace on
demand. Read how updates arrive before deciding.
When your catalogue changes
Section titled “When your catalogue changes”A new product is not in Geoffy until it is crawled. Re-run the crawl from the dashboard after adding products; the pattern is already saved.
Removing a product from your site does not un-publish it. Unpublish it in Geoffy too, or
its plain-text twin stays reachable under /apps/geoffy/llm/{handle}.md on your own
domain.
The rest of the setup is unchanged
Section titled “The rest of the setup is unchanged”Everything else — the root files, the namespace, robots.txt, the IndexNow key — is identical. Follow the quickstart.