Wix-Cart Shared Package
Design Log 07: Wix Cart Shared Package
Written for AI agents. See Log Methodology Note below for details.
Status
Implemented
Background
Cart and checkout code is duplicated between wix-stores (V3) and wix-stores-v1. Both use identical @wix/ecom APIs. Extracting cart functionality to a shared package reduces maintenance burden and ensures consistency.
See Design Log 06 for duplication analysis (~1,350 lines duplicated).
Problem
Maintain two copies of cart code that:
- Use the same
@wix/ecomAPI - Have the same ViewState contracts
- Differ only in service/context naming
Questions & Answers
Q1: Should cart have its own init or piggyback on stores init? A: Cart should have its own init. Other packages consume it via service and context injection.
Q2: How to handle service marker naming?
A: WIX_CART_SERVICE
Q3: Should cart-context use WIX_CLIENT_CONTEXT directly or get injected?
A: It should get WIX_CLIENT_CONTEXT injected.
Design
Package Structure
wix-cart/
├── lib/
│ ├── index.ts # Server exports
│ ├── index.client.ts # Client exports
│ ├── init.ts # Plugin initialization
│ ├── components/
│ │ ├── cart-indicator.ts # Shared cart indicator
│ │ └── cart-page.ts # Shared cart page
│ ├── contexts/
│ │ ├── wix-cart-context.ts # Reactive cart state
│ │ └── cart-helpers.ts # Cart data mapping
│ ├── services/
│ │ └── wix-cart-service.ts # Server-side cart service
│ └── contracts/
│ ├── cart-indicator.jay-contract
│ └── cart-page.jay-contract
├── plugin.yaml
├── package.json
├── tsconfig.json
└── vite.config.ts
Service & Context
// wix-cart-service.ts
export interface WixCartService {
cart: ReturnType<typeof getCurrentCartClient>;
}
export const WIX_CART_SERVICE = createJayService<WixCartService>('Wix Cart Service');
export function provideWixCartService(wixClient: WixClient): WixCartService {
const service: WixCartService = {
cart: getCurrentCartClient(wixClient),
};
registerService(WIX_CART_SERVICE, service);
return service;
}
// wix-cart-context.ts
export const WIX_CART_CONTEXT = createJayContext<WixCartContext>('Wix Cart Context');
// Context receives WIX_CLIENT_CONTEXT during init
export function provideWixCartContext(): WixCartContext {
const wixClientContext = getContext(WIX_CLIENT_CONTEXT);
// ... cart operations using wixClientContext
}
Init Pattern
// wix-cart/lib/init.ts
export const init = makeJayInit()
.withServer(async (): Promise<WixCartInitData> => {
const wixClient = getService(WIX_CLIENT_SERVICE);
provideWixCartService(wixClient);
return { enableClientCart: true };
})
.withClient(async (data: WixCartInitData) => {
const cartContext = provideWixCartContext();
if (data.enableClientCart) {
await cartContext.refreshCartIndicator();
}
});
Consumer Integration
// wix-stores-v1/lib/init.ts (after refactor)
import { init as cartInit } from '@jay-framework/wix-cart';
export const init = makeJayInit()
.withServer(async (): Promise<WixStoresV1InitData> => {
const wixClient = getService(WIX_CLIENT_SERVICE);
provideWixStoresV1Service(wixClient);
// Cart init is automatic via plugin discovery
return { enableClientCart: true, enableClientSearch: true };
})
.withClient(async (data: WixStoresV1InitData) => {
// Cart context already initialized by wix-cart plugin
// Just register stores-specific context
provideWixStoresV1Context();
});
Component Usage
Components use WIX_CART_SERVICE and WIX_CART_CONTEXT:
// wix-cart/lib/components/cart-indicator.ts
export const cartIndicator = makeJayStackComponent()
.withServices(WIX_CART_SERVICE)
.withContract(CartIndicatorContract);
// ...
Implementation Plan
Phase 1: Create wix-cart Package
- Create package directory structure
- Create
package.json,tsconfig.json,vite.config.ts - Create
plugin.yaml - Copy contracts from wix-stores
Phase 2: Core Services
- Create
wix-cart-service.tswithWIX_CART_SERVICE - Create
wix-cart-context.tswithWIX_CART_CONTEXT - Copy
cart-helpers.ts - Create
init.ts
Phase 3: Components
- Adapt
cart-indicator.tsto useWIX_CART_SERVICE - Adapt
cart-page.tsto useWIX_CART_SERVICE - Create entry points (
index.ts,index.client.ts)
Phase 4: Build & Test
- Build wix-cart package
- Verify exports and types
Phase 5: Refactor wix-stores (V3)
- Add
@jay-framework/wix-cartdependency - Remove cart-related files (components, context, helpers)
- Re-export cart components from wix-cart
- Update init.ts to not register cart (wix-cart does it)
- Update wix-stores-context.ts to use WIX_CART_CONTEXT
Phase 6: Refactor wix-stores-v1
- Add
@jay-framework/wix-cartdependency - Remove cart-related files
- Re-export cart components from wix-cart
- Update init.ts
- Update context
Phase 7: Update Examples
- Update store example - add wix-cart dependency if needed
- Update whisky-store example - add wix-cart dependency if needed
- Verify both examples work
Verification Criteria
- ✅ wix-cart package builds successfully
- ✅ wix-stores builds with wix-cart dependency
- ✅ wix-stores-v1 builds with wix-cart dependency
- ✅ store example works unchanged
- ✅ whisky-store example works unchanged
- ✅ Cart operations work (add, update, remove, checkout)
Trade-offs
| Decision | Benefit | Cost |
|---|---|---|
| Separate package | Single source of truth | Additional dependency |
| Own init | Clean separation, works standalone | More init calls |
| Injected WIX_CLIENT_CONTEXT | Flexible, testable | Slightly more complex setup |
Implementation Results
Completed: 2026-01-28
Package Created: @jay-framework/wix-cart at wix/packages/wix-cart/
Files Created:
lib/services/wix-cart-service.ts- Server service withWIX_CART_SERVICElib/contexts/wix-cart-context.ts- Client context withWIX_CART_CONTEXTlib/contexts/cart-helpers.ts- Cart data mapping utilitieslib/components/cart-indicator.ts- Cart indicator componentlib/components/cart-page.ts- Full cart page componentlib/init.ts- Plugin initializationlib/index.ts,lib/index.client.ts- Entry pointslib/contracts/cart-indicator.jay-contract,lib/contracts/cart-page.jay-contract- Contracts
Build Output:
dist/index.js- Server bundle (14.55 KB)dist/index.client.js- Client bundle (7.54 KB)dist/index.d.ts- Type definitions (16.06 KB)
Packages Refactored:
wix-stores (V3):
- Added
@jay-framework/wix-cartdependency - Deleted
cart-indicator.ts,cart-page.ts,cart-helpers.ts - Updated
index.ts/index.client.tsto re-export from wix-cart - Updated
wix-stores-context.tsto delegate cart operations toWIX_CART_CONTEXT - Updated
wix-stores-service.tsto use cart from wix-cart (markedcartas deprecated)
- Added
wix-stores-v1:
- Added
@jay-framework/wix-cartdependency - Deleted
cart-indicator.ts,cart-page.ts,cart-helpers.ts - Updated
index.ts/index.client.tsto re-export from wix-cart - Updated
wix-stores-v1-context.tsto delegate cart operations toWIX_CART_CONTEXT - Updated
wix-stores-v1-service.tsto use cart from wix-cart
- Added
Examples Updated:
store- Added@jay-framework/wix-cart: "workspace:^"dependencywhisky-store- Added@jay-framework/wix-cart: "workspace:^"dependency
Verification:
- ✅
wix-cartpackage builds successfully - ✅
wix-stores(V3) builds with wix-cart dependency - ✅
wix-stores-v1builds with wix-cart dependency - ✅
storeexample validation passes - ✅
whisky-storeexample validation passes
Code Reduction:
- Removed ~1,350 lines of duplicated cart code from wix-stores and wix-stores-v1
- Single source of truth for cart functionality in wix-cart package
Bug Fix: Cart Product URLs Using Wrong Slug (2026-02-27)
Problem: Clicking a product link on the cart page returned 404. For example, product "Aberlour 11y- 55.1%" linked to /products/aberlour-11y-55-1 which was not found.
Root Cause: mapLineItem() in cart-helpers.ts extracted the slug from item.url using item.url.split('/').pop(). The item.url comes from the Wix eCommerce Cart API and defaults to the Wix site's product page URL, whose slug may not match product.slug in the Wix Catalog API. Products with special characters are particularly affected since the two APIs may slugify differently.
Initial fix (reverted): Used catalogReference.catalogItemId (product ID) for cart URLs, with slug-then-ID fallback in product page/action lookups. This worked but produced ugly URLs.
Final fix — set url on LineItem at add-to-cart time:
AddToCartOptionsnow includesproductSlug?: stringaddToCart()inwix-cart-context.tssetsurl: /products/${slug}on the LineItem- The Wix eCommerce API stores this custom URL, so
item.urlcontains our correct slug when reading the cart cart-helpers.tskeeps the originalitem.url.split('/').pop()extraction — it works correctly becauseitem.urlnow has our URL
Changes in consumer packages:
wix-stores-v1-context.ts: always fetches the product to getproduct.slug, passes it asproductSlugwix-stores-context.ts: extractsproduct.slugfrom the already-fetched product, passes it asproductSlug- V3 product page and action: reverted slug-then-ID fallback (no longer needed)
- V1 product page and action: kept slug-then-ID fallback (harmless robustness)
onItemAddedToCart Event (2026-05-15)
The cart context exposes an onItemAddedToCart event (via createEvent<void>()) that fires after addToCart() completes. Components subscribe to it in their interactive phase to react when items are added from anywhere on the page.
Event flow:
- Any component calls
storesContext.addToCart(...)(delegated tocartContext.addToCart()) cartContext.addToCart()calls the Wix eCommerce API, updates the reactive cart indicator, then callsonItemAddedToCart.emit()- All subscribers are notified
Components using the event:
- cart-indicator — flashes a
justAddedflag for 1.5s (CSS animation feedback) - mini-cart — sets
isOpen = trueto auto-open the drawer - cart-page — reloads cart data via
loadCart()to reflect the new item (added 2026-05-15; previously the cart page only loaded on mount, so adding a product from a related-products widget on the same page wouldn't update the cart)
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.