Loading...
Loading...
Apply when deciding, designing, or implementing client-side state with FastStore SDK hooks like useCart, useSession, or useSearch. Covers cart manipulation, session handling, faceted search, and analytics event tracking from @faststore/sdk. Use for any interactive ecommerce feature that relies on FastStore's built-in state management.
npx skill4agent add vtexdocs/ai-skills faststore-state-management@faststore/sdkuseCartuseSessionuseSearchfaststore-themingfaststore-overridesfaststore-data-fetchinguseCart()useSession()validateSessionuseSearch()SearchProvideruseStateuseReducerisValidatinguseCart()sendAnalyticsEvent()@faststore/sdkuseCartuseSessionuseSearchuseCart()useSession()useSearch()useStateuseReduceruseCart()useSession()useSearch()@faststore/sdkcreateContextCartContextSessionContextSearchContext// src/components/MiniCart.tsx
import React from 'react'
import { useCart } from '@faststore/sdk'
export default function MiniCart() {
const { items, totalItems, isValidating, removeItem } = useCart()
if (totalItems === 0) {
return <p>Your cart is empty</p>
}
return (
<div data-fs-mini-cart>
<h3>Cart ({totalItems} items)</h3>
{isValidating && <span>Updating cart...</span>}
<ul>
{items.map((item) => (
<li key={item.id}>
<span>{item.itemOffered.name}</span>
<span>${item.price}</span>
<button onClick={() => removeItem(item.id)}>Remove</button>
</li>
))}
</ul>
</div>
)
}// WRONG: Building a custom cart context instead of using @faststore/sdk
import React, { createContext, useContext, useReducer } from 'react'
interface CartItem {
id: string
name: string
price: number
quantity: number
}
// This custom context duplicates what @faststore/sdk already provides.
// Cart changes here will NOT trigger platform validation.
// Prices and availability will NOT be verified against VTEX.
// Analytics events will NOT fire for add-to-cart actions.
const CartContext = createContext<{
items: CartItem[]
dispatch: React.Dispatch<any>
}>({ items: [], dispatch: () => {} })
function cartReducer(state: CartItem[], action: any) {
switch (action.type) {
case 'ADD':
return [...state, action.payload]
case 'REMOVE':
return state.filter((item) => item.id !== action.payload)
default:
return state
}
}
export function CartProvider({ children }: { children: React.ReactNode }) {
const [items, dispatch] = useReducer(cartReducer, [])
return (
<CartContext.Provider value={{ items, dispatch }}>
{children}
</CartContext.Provider>
)
}isValidatinguseCart()isValidatingtrueisValidatinguseCart()isValidatingisValidating// src/components/CartSummary.tsx
import React from 'react'
import { useCart } from '@faststore/sdk'
export default function CartSummary() {
const { items, totalItems, isValidating } = useCart()
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
return (
<div data-fs-cart-summary>
<p>{totalItems} item{totalItems !== 1 ? 's' : ''} in your cart</p>
<p>Subtotal: ${subtotal.toFixed(2)}</p>
{isValidating && (
<p data-fs-cart-validating>Verifying prices and availability...</p>
)}
<a
href="/checkout"
data-fs-checkout-button
aria-disabled={isValidating}
onClick={(e) => {
if (isValidating) {
e.preventDefault()
}
}}
>
{isValidating ? 'Updating cart...' : 'Proceed to Checkout'}
</a>
</div>
)
}// WRONG: Ignoring cart validation state
import React from 'react'
import { useCart } from '@faststore/sdk'
export default function CartSummary() {
const { items, totalItems } = useCart()
// Missing isValidating — user can click checkout while cart is being validated.
// This can lead to price mismatches at checkout or failed orders.
return (
<div>
<p>{totalItems} items</p>
<a href="/checkout">Proceed to Checkout</a>
{/* No loading state. No validation check. User may proceed with stale prices. */}
</div>
)
}useSession()@faststore/sdklocalStoragesessionStoragevalidateSessionlocalStoragelocalStorage.getItemlocalStorage.setItemuseSession()sessionStorage// src/components/LocaleSwitcher.tsx
import React from 'react'
import { useSession } from '@faststore/sdk'
export default function LocaleSwitcher() {
const { locale, currency, setSession } = useSession()
const handleLocaleChange = (newLocale: string, newCurrency: string) => {
// setSession triggers platform validation and re-fetches data
setSession({
locale: newLocale,
currency: { code: newCurrency, symbol: newCurrency === 'USD' ? '$' : 'R$' },
})
}
return (
<div data-fs-locale-switcher>
<button
onClick={() => handleLocaleChange('en-US', 'USD')}
aria-pressed={locale === 'en-US'}
>
EN
</button>
<button
onClick={() => handleLocaleChange('pt-BR', 'BRL')}
aria-pressed={locale === 'pt-BR'}
>
PT
</button>
<span>Current: {locale} ({currency.code})</span>
</div>
)
}// WRONG: Managing session data manually via localStorage
import React, { useState, useEffect } from 'react'
export default function LocaleSwitcher() {
const [locale, setLocale] = useState('en-US')
useEffect(() => {
// WRONG: Reading session data from localStorage
const saved = localStorage.getItem('store-locale')
if (saved) setLocale(saved)
}, [])
const handleLocaleChange = (newLocale: string) => {
// WRONG: Writing session data to localStorage
// The VTEX platform does NOT know about this change.
// Product prices, availability, and cart will NOT update.
localStorage.setItem('store-locale', newLocale)
setLocale(newLocale)
}
return (
<div>
<button onClick={() => handleLocaleChange('en-US')}>EN</button>
<button onClick={() => handleLocaleChange('pt-BR')}>PT</button>
</div>
)
}// src/components/CartDrawer.tsx
import React from 'react'
import { useCart } from '@faststore/sdk'
import { useSession } from '@faststore/sdk'
import { Button, Loader } from '@faststore/ui'
export default function CartDrawer() {
const { items, totalItems, isValidating, removeItem, updateItemQuantity } =
useCart()
const { currency, locale } = useSession()
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency.code,
})
const subtotal = items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
)
if (totalItems === 0) {
return (
<div data-fs-cart-drawer>
<h2>Your Cart</h2>
<p>Your cart is empty. Start shopping to add items.</p>
</div>
)
}
return (
<div data-fs-cart-drawer>
<h2>Your Cart ({totalItems} items)</h2>
{isValidating && (
<div data-fs-cart-loading>
<Loader />
<span>Verifying prices and availability...</span>
</div>
)}
<ul data-fs-cart-items>
{items.map((item) => (
<li key={item.id} data-fs-cart-item>
<span data-fs-cart-item-name>{item.itemOffered.name}</span>
<span data-fs-cart-item-price>{formatter.format(item.price)}</span>
<div data-fs-cart-item-quantity>
<Button
variant="tertiary"
onClick={() => updateItemQuantity(item.id, item.quantity - 1)}
disabled={item.quantity <= 1}
>
-
</Button>
<span>{item.quantity}</span>
<Button
variant="tertiary"
onClick={() => updateItemQuantity(item.id, item.quantity + 1)}
>
+
</Button>
</div>
<Button variant="tertiary" onClick={() => removeItem(item.id)}>
Remove
</Button>
</li>
))}
</ul>
<div data-fs-cart-summary>
<span>Subtotal: {formatter.format(subtotal)}</span>
<a
href="/checkout"
data-fs-checkout-button
aria-disabled={isValidating}
onClick={(e) => {
if (isValidating) e.preventDefault()
}}
>
{isValidating ? 'Updating cart...' : 'Proceed to Checkout'}
</a>
</div>
</div>
)
}useSearch()// src/components/FacetFilter.tsx
import { useSearch } from '@faststore/sdk'
export default function FacetFilter() {
const { state, setState } = useSearch()
const toggleFacet = (facetKey: string, facetValue: string) => {
const currentFacets = state.selectedFacets || []
const exists = currentFacets.some(
(f) => f.key === facetKey && f.value === facetValue
)
const newFacets = exists
? currentFacets.filter(
(f) => !(f.key === facetKey && f.value === facetValue)
)
: [...currentFacets, { key: facetKey, value: facetValue }]
setState({
...state,
selectedFacets: newFacets,
page: 0, // Reset pagination when filters change
})
}
return (
<div data-fs-facet-filter>
<button onClick={() => toggleFacet('brand', 'Nike')}>
Nike {state.selectedFacets?.some((f) => f.key === 'brand' && f.value === 'Nike') ? '✓' : ''}
</button>
</div>
)
}CartContextuseReducerlocalStoragevalidateSessionuseStateisValidatinguseCart()useCart_unstableuseSession_unstableuseCart()@faststore/sdkuseSession()@faststore/sdkuseSearch()SearchProviderisValidatinglocalStoragesessionStoragefaststore-data-fetching