dist/codex/saleor-commerce/skills/saleor-storefront/SKILL.md
Build Next.js storefronts for Saleor — GraphQL client setup, channel routing, Tailwind CSS, server components, checkout flow, and SEO. Use when developing Saleor storefronts.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins saleor-storefrontInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://docs.saleor.io/docs/developer/storefront for storefront development guidesite:docs.saleor.io storefront GraphQL client setup for client configurationsite:github.com saleor/storefront nextjs for latest storefront starter sourcesite:docs.saleor.io checkout flow storefront for checkout integrationsite:docs.saleor.io channel storefront routing for multi-channel setupSaleor storefronts are headless -- they consume the GraphQL API over HTTP:
| Layer | Component |
|-------|-----------|
| Frontend | Next.js App Router (SSR / RSC) |
| GraphQL Client | urql, Apollo Client, or graphql-request |
| API | Saleor GraphQL endpoint (/graphql/) |
| Styling | Tailwind CSS (official starter) |
| Deployment | Vercel, Netlify, or any Node.js host |
| Client | Strengths | Best For |
|--------|-----------|----------|
| urql | Lightweight, extensible, SSR-friendly | Official starter default |
| Apollo Client | Mature ecosystem, normalized cache | Complex caching needs |
| graphql-request | Minimal, no framework dependency | Simple server-side fetching |
| fetch (raw) | Zero dependencies | One-off queries in server components |
// lib/graphql-client.ts
// Fetch live docs for current client initialization
import { createClient, cacheExchange, fetchExchange } from "urql"
export const client = createClient({
url: process.env.NEXT_PUBLIC_SALEOR_API_URL!,
exchanges: [cacheExchange, fetchExchange],
})
| Variable | Purpose |
|----------|---------|
| NEXT_PUBLIC_SALEOR_API_URL | Saleor GraphQL endpoint URL |
| SALEOR_API_URL | Server-side only API URL (if different) |
| NEXT_PUBLIC_DEFAULT_CHANNEL | Default channel slug |
| NEXT_PUBLIC_STOREFRONT_URL | Public storefront URL (for SEO) |
Saleor channels enable multi-region storefronts from a single instance:
| URL Pattern | Channel | Currency |
|-------------|---------|----------|
| /en-us/products | default-channel | USD |
| /en-gb/products | channel-uk | GBP |
| /de/products | channel-de | EUR |
| Approach | Implementation |
|----------|---------------|
| Path prefix | /[channel]/products in Next.js App Router |
| Subdomain | us.store.com, uk.store.com with middleware |
| Cookie/header | Single URL, channel detected from locale preference |
The channel slug is passed as an argument to every GraphQL query that returns channel-scoped data (products, pricing, collections).
| Page | Route | Data Source |
|------|-------|-------------|
| Homepage | / | Featured products, collections |
| Product listing | /products | products(channel, first, filter) query |
| Product detail | /products/[slug] | product(slug, channel) query |
| Collection | /collections/[slug] | collection(slug, channel) query |
| Category | /categories/[slug] | category(slug) + products query |
| Cart | /cart | Local state + cart GraphQL object |
| Checkout | /checkout | checkout query and mutations |
| Account | /account | me query (authenticated) |
| Order history | /account/orders | me { orders } query |
| Search | /search | products(filter: {search}) query |
| Pattern | Use For | |---------|---------| | Server Component (RSC) | Product listing, product detail, static content, SEO metadata | | Client Component | Cart drawer, quantity selector, add-to-cart button, checkout form | | Server Action | Cart mutations, checkout steps, address submission | | Route Handler | Webhook receivers, revalidation triggers |
// app/products/[slug]/page.tsx
// Fetch live docs for current query patterns
export default async function ProductPage({ params }) {
const { product } = await executeQuery(ProductBySlugDocument, {
slug: params.slug, channel: DEFAULT_CHANNEL,
})
return <ProductTemplate product={product} />
}
| Step | User Action | GraphQL Operation |
|------|-------------|-------------------|
| 1. Create checkout | First add-to-cart | checkoutCreate mutation |
| 2. Add lines | Add products to cart | checkoutLinesAdd mutation |
| 3. Update lines | Change quantity | checkoutLinesUpdate mutation |
| 4. Set email | Enter email | checkoutEmailUpdate mutation |
| 5. Shipping address | Enter address | checkoutShippingAddressUpdate mutation |
| 6. Billing address | Enter or same as shipping | checkoutBillingAddressUpdate mutation |
| 7. Select shipping | Choose method | checkoutDeliveryMethodUpdate mutation |
| 8. Payment | Enter payment details | transactionInitialize or gateway-specific |
| 9. Complete | Confirm order | checkoutComplete mutation |
Checkout ID is stored in a cookie for persistence across sessions and server-side access.
| Strategy | Use Case | Implementation |
|----------|----------|----------------|
| ISR | Product pages | revalidate in fetch options |
| On-demand | After product update webhook | revalidatePath / revalidateTag |
| Client cache | Cart state | urql/Apollo normalized cache |
| Static | Homepage collections | generateStaticParams |
| No cache | Checkout, account | cache: "no-store" in fetch |
| SEO Aspect | Implementation |
|-----------|---------------|
| Title and meta | generateMetadata in page components |
| Open Graph | Product images and descriptions in OG tags |
| Structured data | JSON-LD Product schema in script tags |
| Sitemap | Dynamic sitemap.xml from product/collection queries |
| Canonical URLs | alternates.canonical in metadata |
| Robots | robots.txt via Next.js convention |
| Aspect | Detail |
|--------|--------|
| Source | Saleor media URL from product image fields |
| Optimization | Next.js <Image> component with remote patterns |
| Thumbnails | Saleor generates thumbnails at configurable sizes |
| CDN | Configure next.config.js remotePatterns for Saleor domain |
channel argumentgenerateStaticParams for product and collection pages for SEO and speedFetch the Saleor storefront documentation for exact GraphQL query patterns, checkout mutation sequences, and channel routing strategies before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.