Astro Storefront
Use this guide when building the browser storefront. Start here after the plugin is registered and one product page can call createMika(Astro).
Mika’s default storefront path is Astro-native:
- HTML forms submit to
actions.mika.*. - Product pages use
createMika(Astro). - The host app owns routes, content, layout, localization, auth policy, provider credentials, and deployment.
- Copied components use Kumo-backed UI patterns but remain ordinary host files.
Prerequisites
Section titled “Prerequisites”- Mika is installed or linked as described in Install.
mikaPlugin({ api })or a nativeentrypointis registered inastro.config.mjs.- The host can provide fixture overrides or a real
MikaApiimplementation. - The app can run dynamic routes with
output: "server"or per-routeprerender = false.
Storefront Checklist
Section titled “Storefront Checklist”- Copy the action files and product components.
- Install UI dependencies if you use the Kumo-backed templates.
- Register
server.mika = createMikaActions(). - Render product purchase forms from
createMika(Astro)sellables. - Add host guards for rate limits, account policy, and feature gates.
- Verify that only catalog and stock reads are public plugin JSON routes.
Core Copy Path
Section titled “Core Copy Path”Copy the action files and product components first:
src/actions/index.tssrc/actions/mika.tssrc/styles/kumo.csssrc/lib/routes.tssrc/components/MikaKumoAppFrame.tsxsrc/components/MikaKumoPage.astrosrc/components/ProductPurchase.astrosrc/components/ProductPurchaseSync.astrosrc/components/AddToCartForm.astrosrc/components/AddToCartFormSync.astrosrc/components/BuyNowForm.astrosrc/components/WishlistForm.astrosrc/components/VariantOptionGroups.astrosrc/components/VariantSelector.astrosrc/components/StockBadge.astrosrc/components/LowStockNotice.astrosrc/components/UnavailableNotice.astrosrc/components/ProductStructuredData.astroAdd cart, wishlist, checkout, account, downloads, and webhooks only when the host project needs those flows.
src/lib/routes.ts is included because BuyNowForm.astro uses template checkout defaults (/checkout/success and /checkout/cancel). If the host does not copy those checkout return pages yet, either copy them with the full storefront flow or pass host-owned successPath and cancelPath props when rendering BuyNowForm / ProductPurchase.
UI Dependencies For Copied Templates
Section titled “UI Dependencies For Copied Templates”The copied components use Kumo components, Phosphor icons, and a React island for the app frame. Add these in the host app when copying the template UI:
npm install @cloudflare/kumo @phosphor-icons/react react react-dom @astrojs/reactAdd react() to the host’s existing integrations array; keep emdash(...) and any other integrations already present:
import react from "@astrojs/react";
export default defineConfig({ integrations: [ react(), // Keep the host's existing integrations here. ],});Copy src/styles/kumo.css or import it from your app stylesheet. The UI dependencies belong to the template presentation layer; Mika’s package contracts still remain provider- and layout-neutral.
MikaKumoPage.astro imports ../styles/kumo.css relative to src/components/, so the default destination is src/styles/kumo.css. If you move either file, update that import.
Register The Actions
Section titled “Register The Actions”Copied forms post to actions.mika.*, but that tree exists only once you register it. Copy src/actions/mika.ts (a thin re-export that keeps the factory versioned with Mika) and expose it as server.mika:
export { createMikaActions, mika } from "@bnomei/emdash-mika/astro-actions";import { createMikaActions } from "./mika";
export const server = { mika: createMikaActions() };When the plugin was registered with mikaPlugin({ api }), createMikaActions() resolves that same host API by default; pass createMikaActions({ api }) only to override it.
Two Call Surfaces
Section titled “Two Call Surfaces”Mika exposes the same operations two ways, and copied code picks by context:
- Server reads / resolution —
const Mika = createMika(Astro)from@bnomei/emdash-mika/astro, in page or endpoint frontmatter (Mika.cart.get(),Mika.catalog.sellables(...)). Every call returns a result you branch on:result.ok ? result.data : result.error. - Browser mutations — HTML forms posting to
action={actions.mika.*}withmethod="post", read back withAstro.getActionResult(...).
There are no public browser JSON mutation routes. Only catalog.sellables and stock.availability are exposed as public plugin JSON.
Host Policy With guard
Section titled “Host Policy With guard”Mika does not hard-code rate limits, bot checks, or account gates. Add them through the action guard, and keep Astro’s default security.checkOrigin for the CSRF baseline:
import { ActionError } from "astro:actions";import { createMikaActions } from "./mika";
export const server = { mika: createMikaActions({ guard: async (ctx, action, input) => { if (await isRateLimited(ctx)) { throw new ActionError({ code: "TOO_MANY_REQUESTS", message: "Slow down." }); } }, }),};Keep src/actions/mika.ts as the thin re-export from the copyable template file. Put host-specific policy in src/actions/index.ts, where the host creates the server.mika action tree:
export { createMikaActions, mika } from "@bnomei/emdash-mika/astro-actions";For production, replace isRateLimited(ctx) with host code, for example:
async function isRateLimited(ctx: unknown): Promise<boolean> { // Check the host rate-limit store, bot score, account state, or feature flag. return false;}If a guard needs the operation name, use the action argument to branch:
guard: async (ctx, action, input) => { if (action === "checkoutStart" && await checkoutBlocked(ctx, input)) { throw new ActionError({ code: "FORBIDDEN", message: "Checkout is not available." }); },},Verify
Section titled “Verify”actions.mika.cart.addexists and an add-to-cart form posts without an Action lookup error.Astro.getActionResult(actions.mika.cart.add)can render success or failure feedback./_emdash/api/plugins/mika/catalog/sellables?collection=products&id=<product-id>returns a Mika result envelope for a real host content ref./_emdash/api/plugins/mika/sellables/availability?sellableId=<sellable-id>returns availability for a real sellable.- Cart, checkout, account, webhook, admin, and agent-tool mutations are not exposed as public plugin JSON routes.
Next: Cart, Wishlist, And Checkout covers buyer state and checkout forms. Revisit Product Authoring if sellable, price, stock, or JSON-LD mapping still feels unclear.
Source Anchors
Section titled “Source Anchors”- ⓐ
../emdash-mika/src/templates/astro/examples/astro-storefront.md - ⓐ
../emdash-mika/src/templates/astro/README.md - ⓐ
../emdash-mika/src/templates/astro/actions/index.ts - ⓐ
../emdash-mika/src/templates/astro/actions/mika.ts - ⓐ
../emdash-mika/src/templates/astro/components/ProductPurchase.astro - ⓐ
../emdash-mika/src/templates/astro/components/AddToCartForm.astro - ⓐ
../emdash-mika/src/templates/astro/components/BuyNowForm.astro - ⓐ
../emdash-mika/src/templates/astro/components/MikaKumoPage.astro - ⓐ
../emdash-mika/src/templates/astro/lib/routes.ts - ⓟ
../emdash-mika/src/astro-actions.ts