spree-headless-storefront
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.
Best use case
spree-headless-storefront is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
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.
Teams using spree-headless-storefront should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/spree-headless-storefront/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How spree-headless-storefront Compares
| Feature / Agent | spree-headless-storefront | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
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.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Spree Headless Next.js Storefront
## Before writing code
**Fetch live docs**:
1. Fetch https://github.com/spree/storefront (README) for current setup and customization.
2. Fetch https://spreecommerce.org/docs/developer/storefront/nextjs/quickstart for the quickstart.
3. Fetch the Next.js 16 docs for App Router / Server Actions patterns the storefront uses.
4. Cross-reference https://github.com/spree/sdk for SDK usage.
5. Check the v5.4 announcement for the storefront's release context.
## Conceptual Architecture
### The Two Repos
| Repo | Role |
|------|------|
| `spree/spree-starter` | Rails backend application |
| `spree/storefront` | Next.js headless storefront |
The storefront talks to the backend exclusively over API v3 (Store API).
### Tech Stack
| Layer | Tech |
|-------|------|
| Framework | Next.js 16 (App Router, Server Actions, Turbopack) |
| UI runtime | React 19 (Server Components + Client Components) |
| Styling | Tailwind CSS 4 |
| Type system | TypeScript 5 |
| API client | `@spree/sdk` with Zod schemas |
| Search | MeiliSearch faceted search |
| Auth | Server-only — httpOnly JWT cookies + publishable key |
| Payments | Stripe Elements / Apple Pay / Google Pay / Link / Klarna / Affirm / SEPA |
| Analytics | GA4, JSON-LD, OpenGraph |
| Error tracking | Sentry |
| Deploy | Vercel (recommended) or Docker |
### Prerequisites
- Node 20+
- Spree backend on **5.4+** (API v3 required)
- A publishable key (`pk_…`) from admin
### Project Layout
```
storefront/
├── app/
│ ├── (storefront)/ # public pages (App Router groups)
│ │ ├── [region]/[locale]/
│ │ │ ├── products/
│ │ │ ├── cart/
│ │ │ └── checkout/
│ │ └── account/
│ ├── api/ # Route Handlers for webhooks etc.
│ ├── layout.tsx
│ └── page.tsx
├── components/
│ ├── ui/ # Tailwind-styled primitives
│ ├── product/
│ ├── cart/
│ └── checkout/
├── lib/
│ ├── spree.server.ts # admin/auth client — never imported client-side
│ ├── spree.public.ts # publishable-key client
│ ├── auth.ts # Server Action helpers for sign in/out
│ └── meilisearch.ts
├── public/
├── tailwind.config.ts
├── next.config.ts
└── package.json
```
### Auth: Server-Only by Design
- The **publishable key** (`pk_…`) is the only Spree credential that may reach the browser.
- **User JWTs** live in **httpOnly cookies** set via Server Actions; never accessible to client JS.
- The **admin API key** never leaves the server bundle. The storefront should never have it set in env.
- Cart `order_token` also in httpOnly cookie.
```typescript
// app/api/auth/sign-in/route.ts (Route Handler)
import { cookies } from 'next/headers';
import { spreePublic } from '@/lib/spree.public';
export async function POST(req: Request) {
const { email, password } = await req.json();
const { access_token, refresh_token, expires_in } = await spreePublic.auth.signIn({ email, password });
cookies().set('spree_jwt', access_token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: expires_in,
});
cookies().set('spree_refresh', refresh_token, { httpOnly: true, secure: true, sameSite: 'lax' });
return Response.json({ ok: true });
}
```
### Market / Region Routing
URLs follow `/[region]/[locale]/...`:
- `/us/en/products/classic-tee`
- `/de/de/produkte/classic-tee`
The storefront resolves `current_market` from the URL, then passes it to the API so backend filters by market-allowed payment methods, shipping methods, prices.
### Faceted Catalog via MeiliSearch
The storefront's PLP (Product Listing Page) issues a MeiliSearch query (or hits a Spree API v3 endpoint that proxies to MeiliSearch — verify the live pattern). Facets: brand, color, size, price range, taxon. Typo-tolerant.
### Checkout Flow
1. **Cart page** — line items, totals, suggested upsells
2. **Address page** — bill/ship; pre-filled for logged-in users
3. **Delivery page** — pick shipping method (one per shipment)
4. **Payment page** — pick payment method, render gateway UI
- Stripe → Stripe Elements / Apple Pay / Google Pay / Link
- Klarna, Affirm, SEPA — Stripe-backed
5. **Review page** — final totals, place order
6. **Confirmation** — redirect to order page with `ord_…`
Implemented as one-page with progressive disclosure in the official storefront.
### Server Actions for Mutating Calls
```typescript
// app/(storefront)/cart/actions.ts
'use server';
import { cookies } from 'next/headers';
import { revalidatePath } from 'next/cache';
import { spreePublic } from '@/lib/spree.public';
export async function addToCart(variantId: string, quantity = 1) {
const orderToken = cookies().get('spree_cart_token')?.value;
const result = await spreePublic.cart.addItem({ variantId, quantity, orderToken });
if (!orderToken) {
cookies().set('spree_cart_token', result.token, { httpOnly: true, secure: true, sameSite: 'lax' });
}
revalidatePath('/cart');
return result;
}
```
### SEO
- **JSON-LD** for Product, BreadcrumbList, Offer, Organization
- **OpenGraph** + Twitter Cards
- **Sitemap** generated by `next-sitemap` or similar
- **robots.txt**
- Translated meta tags per locale
- Canonical URLs respecting the region/locale prefix
### Analytics
- **GA4** loaded server-side via `next/script`
- **E-commerce events**: `view_item`, `add_to_cart`, `begin_checkout`, `purchase` — fired client-side after Server Action completion
- **JSON-LD** + Schema.org for structured data
## Implementation Guidance
### Cloning and Running
```bash
git clone https://github.com/spree/storefront my-storefront
cd my-storefront
cp .env.example .env.local
# Set:
# SPREE_API_URL=http://localhost:4000 (your Spree backend)
# NEXT_PUBLIC_SPREE_PUBLISHABLE_KEY=pk_test_...
# MEILISEARCH_HOST=http://localhost:7700
# MEILISEARCH_PUBLIC_KEY=...
npm install
npm run dev # http://localhost:3000
```
### Customizing Pages
The App Router structure makes customization file-system-based — add or replace files under `app/`. For shared layout changes, edit the relevant `layout.tsx`.
### Customizing Components
Override Tailwind tokens in `tailwind.config.ts`:
```typescript
export default {
theme: {
extend: {
colors: {
brand: { 500: '#0066ff', 600: '#0052cc' }
},
fontFamily: {
display: ['"Inter Display"', 'sans-serif']
}
}
}
}
```
For Tailwind 4 specifically, prefer the `@theme` directive in CSS:
```css
@import 'tailwindcss';
@theme {
--color-brand-500: oklch(0.6 0.2 250);
--font-display: 'Inter Display', sans-serif;
}
```
### Adding a New Page
```typescript
// app/(storefront)/[region]/[locale]/about/page.tsx
export default async function AboutPage({ params }: { params: Promise<{ region: string; locale: string }> }) {
const { locale } = await params;
return <main>About {locale}</main>;
}
```
Next.js 16 wraps `params` in a Promise — `await` it.
### Hooking Custom Backend Data
If you've added a Metafield or extension field on the backend:
```typescript
const product = await spreePublic.products.find('prod_...', { expand: ['metafields'] });
const launchDate = product.metafields?.my_app?.launch_date;
```
Augment SDK types via `types/spree.d.ts` if you want type safety.
### Caching Strategy
| Page | Strategy |
|------|----------|
| Homepage | ISR — `revalidate: 60` |
| Product detail | ISR — `revalidate: 300` (5 min) |
| Cart | Dynamic — `dynamic = 'force-dynamic'` |
| Account | Dynamic |
| Order confirmation | Dynamic |
### Deployment
**Vercel (recommended)**:
- Hook the GitHub repo
- Set env vars (`SPREE_API_URL`, `NEXT_PUBLIC_SPREE_PUBLISHABLE_KEY`, MeiliSearch creds, Sentry DSN)
- Build command: `npm run build`
- Output: standalone
**Docker**:
- The repo ships a `Dockerfile`
- Multi-stage build to reduce image size
- Deploy to any container runtime
### Common Pitfalls
- **Importing `spree.server.ts` from a Client Component** — Next.js's `'use server'` enforcement is essential; opt into it.
- **Storing tokens in localStorage** — XSS-vulnerable; use httpOnly cookies via Server Actions.
- **Hardcoding region/locale** — breaks multi-market deployments.
- **Forgetting `revalidatePath` after Server Actions** — stale UI.
- **MeiliSearch index drift** — backend reindexes async; staging vs prod can disagree.
- **Hardcoding payment-method visibility in UI** — let the backend's Market config drive it.
- **Bundling Sentry on the client with the server DSN** — leaks credentials. Use the public DSN.
- **Pinning Next.js too aggressively** — the storefront repo bumps Next.js minor versions; match the storefront's pin.
Always re-fetch the storefront repo's README before customizing — the codebase iterates rapidly with each Spree release.Related Skills
spree-upgrades
Upgrade a Spree application — version-to-version migration paths (v4 → v5 is a major rewrite; v5.x → v5.y are simpler), Gemfile pinning strategy, decorator brittleness across versions, deprecated gems to remove (`spree_auth_devise`, `deface`, `spree_backend`), API v2 → v3 migration via `spree_legacy_api_v2`, admin Bootstrap → Tailwind transition, the upgrade guide workflow, and rollback strategy. Use when planning a Spree upgrade or auditing technical debt blocking one.
spree-typescript-sdk
Build storefronts and integrations using `@spree/sdk` — the official TypeScript SDK for Spree's API v3 (v5.4+). Covers installation, the resource-builder pattern (`spree.products.list`, `spree.checkout.create`, etc.), Zod schema validation, server-only auth (httpOnly JWTs, never exposing API keys), error handling, typing customizations, and pinning SDK versions to backend Spree releases. Use when integrating Spree into a Next.js / Node.js / TypeScript project.
spree-testing
Test Spree applications and extensions with RSpec — `spree_dev_tools` gem (v5.2+) for factories and helpers, FactoryBot patterns (prefer `build` over `create`), Capybara feature specs, controller/request specs, testing decorators and subscribers, dummy app for extension testing, system specs for the Hotwire admin, and CI patterns. Use when writing Spree tests, setting up CI, or refactoring slow specs.
spree-shipping-fulfillment
Build and customize Spree's shipping and fulfillment — ShippingMethod, ShippingCategory, Zone/ZoneMember, ShippingRate, the Stock::Estimator service, StockLocation/StockItem/StockMovement, multi-shipment orders, ShippingCalculator classes (FlatRate, FlatPercentItemTotal, PerItem, FlexiRate), shipment state machine, returns (ReturnAuthorization → CustomerReturn → Reimbursement → Refund), and integrating carrier APIs (UPS, FedEx, ShipStation). Use when configuring shipping rules, building fulfillment integrations, or debugging shipping-rate calculations.
spree-setup
Bootstrap a new Spree project — `create-spree-app` CLI (v5.2+), `spree-starter` Rails backend, the Next.js storefront repo, `bin/rails g spree:install`, sample data, Docker Compose, and the PostgreSQL + Redis + Sidekiq prerequisites. Use when starting a new Spree project from scratch or onboarding an existing repo.
spree-security
Secure a Spree deployment — Rails credentials and env-var hygiene, Devise auth (Spree v5 ships it in-core; `spree_auth_devise` is archived), CanCanCan authorization rules, Doorkeeper OAuth2 scopes, Storefront publishable key vs admin API key, webhook HMAC verification, OWASP Top 10 for Rails (mass assignment, CSRF, SQL injection via Ransack, XSS, IDOR through prefixed IDs), PCI scope (Spree never touches raw cards thanks to gateway tokenization), and multi-store data isolation. Use when auditing a Spree app, hardening a deploy, or addressing a security incident.
spree-promotions
Build and customize Spree's promotions engine — Promotion + PromotionRule + PromotionAction + CouponCode + Adjustment, the bundled rules (FirstOrder/ItemTotal/Product/Taxon/User/OneUsePerUser/Country/CustomerGroup/etc.), bundled actions (CreateAdjustment/CreateItemAdjustments/FreeShipping/CreateLineItems), Calculator classes, coupon batches with CSV export, the v5.1+ advanced rule-based engine, and authoring custom rules/actions/calculators. Use when modeling promotions, building discount UIs, or extending the promotions engine.
spree-performance
Profile and optimize a Spree application — N+1 queries with bullet/scout, database indexing strategy for Spree's polymorphic associations, Rails fragment + Russian doll caching, ActiveStorage variant pre-generation, Sidekiq queue tuning, MeiliSearch vs Postgres FTS tradeoffs, Puma worker/thread sizing, CDN strategy for catalog pages, asset precompile time, and load testing. Use when Spree is slow, the database is hot, or you're preparing for a traffic spike (Black Friday, launch).
spree-payments
Integrate payment gateways with Spree — PaymentMethod model, the v5.4+ PaymentSession provider-agnostic checkout flow, Stripe via `spree_stripe` (Apple/Google Pay, Link, Connect for marketplaces), Adyen via `spree_adyen`, PayPal via `spree_paypal_checkout`, StoreCredit / GiftCard as payment methods, refunds, payment state machine, and authoring a custom gateway. Use when wiring a payment integration, handling webhooks from a gateway, or debugging payment-state issues.
spree-multi-store
Configure Spree for multi-store and multi-region commerce — one Rails install running many `Store` records, the v5.4+ `Market` model (currency + locale + payment methods + shipping per region), what's shared vs per-store (products+inventory+customers shared; orders+payments+themes per-store), the Marketplace module (Enterprise — vendors/commission/payouts via Stripe Connect), and the Multi-tenant SaaS model. Use when planning a multi-brand or multi-region Spree deployment.
spree-legacy-api-v2
Work with Spree's legacy v2 APIs — JSON:API-style Storefront API at `/api/v2/storefront/*` and Platform API at `/api/v2/platform/*`, Doorkeeper OAuth2 (password grant for storefront, client_credentials + admin scope for platform), the `spree_legacy_api_v2` gem (required in v5+ for backward compatibility), and migration patterns to API v3. Use when maintaining a v2 client during a migration window, integrating an older partner system, or deciding when to cut over to v3.
spree-i18n
Localize a Spree application — the `spree_i18n` gem with 60+ locale packs, the v5.4+ Translations Center for product/CMS content (CSV import/export), Rails i18n basics applied to Spree (translation files, locale switching, pluralization, interpolation), per-Market locale routing in the headless storefront, RTL languages, and translating extensions. Use when localizing a Spree store, adding a new locale, or building i18n-aware extensions.