Framed Canvas — $24
``` > **Note:** Using the SDK? `getMockupUrl()` and `getBuyUrl()` build these two URLs from the > same options object — see [the SDK page](https://developers.snowcone.app/sdk). ## Use the real price Hard-coding `$24` works for one product, but the price lives in the public catalog — read it so it never drifts. The checkout always resolves price from your Shop ID, never the URL. ```tsx // Don't hard-code the price — read it from the public catalog. const res = await fetch( "https://search.snowcone.app/indexes/snowcone/documents/BEEB77", { headers: { Authorization: "Bearer eee819b849798ad9091228c486ec05d0931e5292" } }, ); const { name, price } = await res.json(); // price is MSRP, in cents ``` ## Next That's a complete sale on a static page. From here: [add size & color options](https://developers.snowcone.app/catalog/options), let shoppers [customize the artwork](https://developers.snowcone.app/canvas), or build the whole thing in [Next.js](https://developers.snowcone.app/guides/storefront-nextjs). # A storefront with Next.js > Read the catalog server-side, render mockups, and wire a working buy flow into a Next.js > app — grounded in the URL primitive the whole way. (Guides · 20 min) This is the markdown mirror of https://developers.snowcone.app/guides/storefront-nextjs — the same content as the page, served as plain text for agents and non-JS clients. This builds a real storefront: a grid of products from the public catalog, each rendered as a live mockup, each linking to checkout. It uses the SDK for ergonomics, but everything underneath is the same `img`/`buy` URL you can build by hand. ## Install & configure Add the SDK (and the optional UI components for later). Then set your public Shop ID — it's safe in a `NEXT_PUBLIC_` var. ```bash pnpm add @snowcone-app/sdk @snowcone-app/ui ``` ```bash # .env.local — the Shop ID is public, so NEXT_PUBLIC_ is fine. NEXT_PUBLIC_SNOWCONE_SHOP=YOUR_SHOP_ID ``` ## List the catalog The catalog is a public Meilisearch index, so a Server Component can fetch it directly — no SDK required to read it. Cache it; the catalog changes rarely. ```tsx // app/page.tsx — a Server Component. Read the catalog at request time. // The catalog is a public Meilisearch index; the search key is public. const SEARCH = "https://search.snowcone.app/indexes/snowcone"; const KEY = "eee819b849798ad9091228c486ec05d0931e5292"; async function getProducts() { const res = await fetch(`${SEARCH}/search`, { method: "POST", headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ limit: 24 }), next: { revalidate: 3600 }, // cache the catalog for an hour }); const { hits } = await res.json(); return hits as { id: string; name: string; price: number }[]; } export default async function Storefront() { const products = await getProducts(); return ({product.name} — ${(product.price / 100).toFixed(2)}
{stage}…
}{part.text}
; // Generated artwork this turn — show it, let the user react to it. case "data-images": return part.data.images.map(({ imageUrl }) => (Total artworks: {artworks.length}