Skip to content

First Product Page

Use this page after Mika is registered and actions.mika.* exists. It shows the smallest host-owned product page shape: resolve host content, ask Mika for sellables, render product metadata, then render purchase forms.

Product pages stay host-owned. Mika supplies the request-bound helper, form contracts, sellable data, and copyable purchase components.

  • Mika is registered in the host EmDash config.
  • createMikaActions() is exported as server.mika.
  • The host can look up a product by route slug.
  • The product purchase component dependency set from First Integration Checklist is copied, including src/lib/routes.ts; ProductStructuredData.astro is copied too.
  • If rendering copied Kumo-backed components, @cloudflare/kumo, React, @astrojs/react, and src/styles/kumo.css are installed and imported.
  • The product route can render on demand with export const prerender = false or server output.

The usual first page does this:

  1. Fetch the host product entry.
  2. Create a request-bound Mika helper with createMika(Astro).
  3. Load sellables with Mika.catalog.sellables(collection, id).
  4. Render visible host product content.
  5. Render ProductStructuredData with matching product and offer metadata.
  6. Render ProductPurchase for variants, stock state, add-to-cart, buy-now, and wishlist actions.

Map Host Product Routes To Mika Content Refs

Section titled “Map Host Product Routes To Mika Content Refs”

The URL slug is host-owned. Mika sellable lookup uses a content ref, usually a collection name plus the host entry ID.

Resolve the slug to a host product first, then pass the stable content ref to Mika:

const product = productBySlug(Astro.params.slug);
const sellablesResult = await Mika.catalog.sellables("products", product.id);

Do not assume the route slug and Mika content ID are the same value. The seeded template route /products/[slug].astro resolves the slug with templateProductBySlug(slug), then calls Mika.catalog.sellables("products", product.id).

The sample below is host-app pseudocode for src/pages/products/[slug].astro.

---
import { createMika } from "@bnomei/emdash-mika/astro";
import ProductPurchase from "../../components/ProductPurchase.astro";
import ProductStructuredData from "../../components/ProductStructuredData.astro";
import { productBySlug } from "../../lib/products";
export const prerender = false;
const Mika = createMika(Astro);
const slug = Astro.params["slug"];
if (!slug) return Astro.redirect("/404");
const product = productBySlug(slug);
if (!product) return Astro.redirect("/404");
const sellablesResult = await Mika.catalog.sellables("products", product.id);
const sellables = sellablesResult.ok ? sellablesResult.data : [];
---
<ProductStructuredData
sellables={sellables}
product={{ name: product.title, description: product.description, url: Astro.url }}
/>
<ProductPurchase sellables={sellables} />

The seeded template uses the same boundary, but its lookup is templateProductBySlug(slug) from src/lib/mika-api.ts and its route passes fixture overrides with createMika(Astro, { api: mikaApiOverrides }) so the page reads the resettable template store instead of production repositories.

The same page also receives the POST results from the purchase forms. Read them with Astro.getActionResult(), and redirect to the provider when a checkout starts:

---
import { actions } from "astro:actions";
const checkout = Astro.getActionResult(actions.mika.checkout.start);
if (checkout?.data?.redirectUrl) return Astro.redirect(checkout.data.redirectUrl);
const added = Astro.getActionResult(actions.mika.cart.add); // CartDTO on success
---

ProductPurchase owns the add-to-cart, buy-now, and wishlist forms. It derives its view model with createMikaPurchaseModel(sellables) (selected sellable/price, maxQuantity, unavailable, grouped-variant controls), so you do not hand-assemble those controls.

  • The page renders host product copy and Mika purchase controls from the same content ref.
  • ProductStructuredData receives the same sellables shown to buyers.
  • Add-to-cart submits to actions.mika.cart.add and Astro.getActionResult() can render the result.
  • Checkout start either redirects to a provider session or returns a clear provider/configuration error.

Next: use Astro Storefront to copy the full action and component set, or use Cart, Wishlist, And Checkout once the product form posts successfully.

  • ../emdash-mika/src/templates/astro/examples/astro-storefront.md
  • ../emdash-mika/src/templates/astro/components/ProductPurchase.astro
  • ../emdash-mika/src/templates/astro/components/ProductStructuredData.astro
  • ../emdash-mika/src/astro.ts
  • ../emdash-mika-template/src/pages/products/[slug].astro
  • ../emdash-mika-template/src/lib/mika-api.ts
  • ../emdash-mika-template/src/lib/mika-fixture-storefront.ts