
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@shopkit/cart
Advanced tools
Cart state management for e-commerce storefronts.
npm install @shopkit/cart
# or
bun add @shopkit/cart
// app/providers.tsx or similar
import {
configureCart,
DefaultCartService,
DefaultCartStorage,
} from '@shopkit/cart';
// Configure once at app initialization
configureCart({
merchantName: 'my-store',
cartService: new DefaultCartService('/api/cart'),
cartStorage: new DefaultCartStorage('my-store'),
defaultCurrencyCode: 'USD',
onCartEvent: (event) => {
console.log('Cart event:', event);
},
});
// app/layout.tsx
import { CartInitializer } from '@shopkit/cart';
export default function RootLayout({ children }) {
return (
<html>
<body>
<CartInitializer merchantName="my-store" />
{children}
</body>
</html>
);
}
import { useCartStore, mapProductToCartItem, useCartCount, useCartToggle } from '@shopkit/cart';
function ProductCard({ product }) {
const { addItem, loadingItems } = useCartStore();
const cartCount = useCartCount();
const toggleCart = useCartToggle();
const variantId = product.variant?.id || product.variantId;
const isLoading = loadingItems[variantId] || false;
const handleAddToCart = () => {
const cartItem = mapProductToCartItem(product);
addItem(cartItem);
};
return (
<div>
<h3>{product.title}</h3>
<button onClick={handleAddToCart} disabled={isLoading}>
{isLoading ? 'Adding...' : 'Add to Cart'}
</button>
<button onClick={toggleCart}>
Cart ({cartCount})
</button>
</div>
);
}
Note: Developers are responsible for mapping product data to the correct
CartItemformat. Use themapProductToCartItemutility for common product structures.
configureCart(config: CartConfig)Configure the cart module. Must be called before using any cart functionality.
interface CartConfig {
merchantName: string;
cartService: ICartService;
cartStorage: ICartStorage;
defaultCurrencyCode?: string;
onCartEvent?: (event: CartEvent) => void;
}
useCartStore()The main hook for all cart operations. Provides access to cart state and actions.
State:
| Property | Type | Description |
|---|---|---|
items | CartItem[] | Items currently in the cart |
isOpen | boolean | Whether the cart drawer/modal is open |
isLoading | boolean | Whether any cart operation is in progress |
loadingItems | Record<string, boolean> | Per-item loading states keyed by variantId |
error | string | null | Current error message, if any |
cartToken | string | null | Cart token for API authentication |
checkoutUrl | string | undefined | URL to proceed to checkout |
totalAmount | { amount: number; currencyCode: string } | undefined | Server-calculated total |
isInitialized | boolean | Whether the cart has been initialized |
Actions:
| Method | Signature | Description |
|---|---|---|
initializeCart | (merchantName?: string) => Promise<void> | Initialize the cart |
addItem | (item: CartItem) => Promise<void> | Add a single item to cart |
addItems | (items: CartItemRequest[]) => Promise<void> | Add multiple items to cart |
removeItem | (lineId: string, variantId?: string) => Promise<void> | Remove an item from cart |
incrementItem | (lineId: string, variantId?: string) => Promise<void> | Increment item quantity by 1 |
decrementItem | (lineId: string, variantId?: string) => Promise<void> | Decrement item quantity by 1 |
setItemQuantity | (lineId: string, quantity: number, variantId?: string) => Promise<void> | Set item quantity directly |
clearCart | () => Promise<void> | Clear cart after purchase: removes stale token from localStorage, resets in-memory state, and immediately creates a fresh cart |
openCart | () => Promise<void> | Open the cart drawer/modal |
closeCart | () => void | Close the cart drawer/modal |
toggleCart | () => Promise<void> | Toggle cart open/closed |
Example - Cart Drawer:
function CartDrawer() {
const { items, isOpen, closeCart, removeItem, incrementItem, decrementItem, loadingItems } = useCartStore();
if (!isOpen) return null;
return (
<div className="cart-drawer">
<button onClick={closeCart}>Close</button>
{items.map((item) => {
const isItemLoading = loadingItems[item.variantId || item.id] || false;
return (
<div key={item.id}>
<span>{item.title}</span>
<button onClick={() => decrementItem(item.id, item.variantId)} disabled={isItemLoading}>-</button>
<span>{item.quantity}</span>
<button onClick={() => incrementItem(item.id, item.variantId)} disabled={isItemLoading}>+</button>
<button onClick={() => removeItem(item.id, item.variantId)} disabled={isItemLoading}>Remove</button>
</div>
);
})}
</div>
);
}
The loadingItems property tracks loading state for each item individually, allowing you to show loading indicators only for the specific item being modified.
const { loadingItems, addItem, incrementItem } = useCartStore();
// Check if a specific product is loading
const isProductLoading = (variantId: string) => loadingItems[variantId] || false;
// Use in component
function ProductButton({ product }) {
const variantId = product.variant?.id || product.variantId;
const isLoading = isProductLoading(variantId);
return (
<button disabled={isLoading}>
{isLoading ? <Spinner /> : 'Add to Cart'}
</button>
);
}
Convenience hooks for specific state slices:
| Hook | Return Type | Description |
|---|---|---|
useCartCount() | number | Number of unique items in cart |
useCartTotalQuantity() | number | Total quantity of all items |
useCartItems() | CartItem[] | Array of cart items |
useCartToggle() | () => void | Function to toggle cart open/closed |
useCartStatus() | { isLoading, error } | Loading and error state |
useCartTotal() | number | Client-calculated total price |
useCartTotalAmount() | { amount, currencyCode } | undefined | Server-calculated total |
useIsInCart(productId, variantId?) | boolean | Check if product is in cart |
useCartItem(productId, variantId?) | CartItem | undefined | Get specific cart item |
useCheckoutUrl() | string | undefined | Get checkout URL |
mapProductToCartItem(product, variantId?, quantity?, currencyCode?)Map a product object to a CartItem. Use this to convert your product data structure to the format required by the cart.
import { mapProductToCartItem } from '@shopkit/cart';
const cartItem = mapProductToCartItem(product);
addItem(cartItem);
formatPrice(amount, currencyCode?, locale?)Format a price amount for display.
formatPrice(29.99, 'USD', 'en-US'); // "$29.99"
calculateCartTotal(items)Calculate the total price of cart items (client-side).
calculateSavings(items)Calculate total savings from compare-at prices.
DefaultCartServiceDefault HTTP-based cart service implementation.
const service = new DefaultCartService('/api/cart');
DefaultCartStorageDefault localStorage-based cart storage.
const storage = new DefaultCartStorage('my-store');
<CartInitializer merchantName="..." />Initialize the cart on mount. Must be rendered after configureCart() is called.
Implement ICartService for custom cart backends:
import { ICartService, CartResponse, CartItemRequest } from '@shopkit/cart';
class MyCartService implements ICartService {
async createCart(merchantName: string): Promise<CartResponse> {
// Your implementation
}
async getCart(cartId: string): Promise<CartResponse> {
// Your implementation
}
async addToCart(cartId: string, items: CartItemRequest[]): Promise<void> {
// Your implementation
}
async removeFromCart(cartId: string, itemIds: string[]): Promise<void> {
// Your implementation
}
async updateCart(cartId: string, items: CartItemRequest[]): Promise<void> {
// Your implementation
}
}
MIT
FAQs
Cart state management, hooks, and services for e-commerce storefronts
We found that @shopkit/cart demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.