Skip to content

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”
  1. Geoffy reads your sitemap and proposes URL patterns that look like product pages. You need a working sitemap.xml first — @astrojs/sitemap is the usual answer.

  2. You confirm the pattern, for example /products/:handle. The segment it captures becomes each product’s handle.

  3. Geoffy crawls the matching pages and records what it could read about each product.

  4. You review what it found — per product, including the canonical URL each page declares.

  5. You publish the products worth publishing.

The crawl reads what your HTML says, so what Geoffy has to work with is decided by your product page:

Give itWhy
A real <h1> with the product nameThe strongest signal of what the page is about
A <link rel="canonical"> naming this productWithout it the product is unpublishable — canonical_broken
Product facts as text, not only in imagesA specification table in a JPEG is invisible
Your own Product structured data, if you have itGeoffy supplements it rather than duplicating it
src/pages/products/[handle].astro
---
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.

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.

Everything else — the root files, the namespace, robots.txt, the IndexNow key — is identical. Follow the quickstart.