The commerce framework ships multiple hooks and a Node.js API, both using an underlying headless e-commerce platform, which we call commerce providers.
The core features are:
- Code splitted hooks for data fetching using SWR, and to handle common user actions
- A Node.js API for initial data population, static generation of content and for creating the API endpoints that connect to the hooks, if required.
👩🔬 If you would like to contribute a new provider, check the docs for Adding a new Commerce Provider.
🚧 The core commerce framework is under active development, new features and updates will be continuously added over time. Breaking changes are expected while we finish the API.
A commerce hook is a React hook that's connected to a commerce provider. They focus on user actions and data fetching of data that wasn't statically generated.
Data fetching hooks use SWR underneath and you're welcome to use any of its return values and options. For example, using the useCustomer
hook:
const { data, isLoading, error } = useCustomer({
swrOptions: {
revalidateOnFocus: true,
},
})
This component adds the provider config and handlers to the context of your React tree for it's children. You can optionally pass the locale
to it:
import { CommerceProvider } from '@framework'
const App = ({ locale = 'en-US', children }) => {
return <CommerceProvider locale={locale}>{children}</CommerceProvider>
}
Returns a signup function that can be used to sign up the current visitor:
import useSignup from '@framework/auth/use-signup'
const SignupView = () => {
const signup = useSignup()
const handleSignup = async () => {
await signup({
email,
firstName,
lastName,
password,
})
}
return <form onSubmit={handleSignup}>{children}</form>
}
Returns a login function that can be used to sign in the current visitor into an existing customer:
import useLogin from '@framework/auth/use-login'
const LoginView = () => {
const login = useLogin()
const handleLogin = async () => {
await login({
email,
password,
})
}
return <form onSubmit={handleLogin}>{children}</form>
}
Returns a logout function that signs out the current customer when called.
import useLogout from '@framework/auth/use-logout'
const LogoutButton = () => {
const logout = useLogout()
return (
<button type="button" onClick={() => logout()}>
Logout
</button>
)
}
Fetches and returns the data of the signed in customer:
import useCustomer from '@framework/customer/use-customer'
const Profile = () => {
const { data, isLoading, error } = useCustomer()
if (isLoading) return <p>Loading...</p>
if (error) return <p>{error.message}</p>
if (!data) return null
return <div>Hello, {data.firstName}</div>
}
Helper hook to format price according to the commerce locale and currency code. It also handles discounts:
import useCart from '@framework/cart/use-cart'
import usePrice from '@framework/product/use-price'
// ...
const { data } = useCart()
const { price, discount, basePrice } = usePrice(
data && {
amount: data.subtotalPrice,
currencyCode: data.currency.code,
// If `baseAmount` is used, a discount will be calculated
// baseAmount: number,
}
)
// ...
Fetches and returns the products that match a set of filters:
import useSearch from '@framework/product/use-search'
const SearchPage = ({ searchString, category, brand, sortStr }) => {
const { data } = useSearch({
search: searchString || '',
categoryId: category?.entityId,
brandId: brand?.entityId,
sort: sortStr,
})
return (
<Grid layout="normal">
{data.products.map((product) => (
<ProductCard key={product.path} product={product} />
))}
</Grid>
)
}
Fetches and returns the data of the current cart:
import useCart from '@framework/cart/use-cart'
const CartTotal = () => {
const { data, isLoading, isEmpty, error } = useCart()
if (isLoading) return <p>Loading...</p>
if (error) return <p>{error.message}</p>
if (isEmpty) return <p>The cart is empty</p>
return <p>The cart total is {data.totalPrice}</p>
}
Returns a function that adds a new item to the cart when called, if this is the first item it will create the cart:
import { useAddItem } from '@framework/cart'
const AddToCartButton = ({ productId, variantId }) => {
const addItem = useAddItem()
const addToCart = async () => {
await addItem({
productId,
variantId,
})
}
return <button onClick={addToCart}>Add To Cart</button>
}
Returns a function that updates a current item in the cart when called, usually the quantity.
import { useUpdateItem } from '@framework/cart'
const CartItemQuantity = ({ item }) => {
const [quantity, setQuantity] = useState(item.quantity)
const updateItem = useUpdateItem({ item })
const updateQuantity = async (e) => {
const val = e.target.value
setQuantity(val)
await updateItem({ quantity: val })
}
return (
<input
type="number"
max={99}
min={0}
value={quantity}
onChange={updateQuantity}
/>
)
}
If the quantity
is lower than 1 the item will be removed from the cart.
Returns a function that removes an item in the cart when called:
import { useRemoveItem } from '@framework/cart'
const RemoveButton = ({ item }) => {
const removeItem = useRemoveItem()
const handleRemove = async () => {
await removeItem(item)
}
return <button onClick={handleRemove}>Remove</button>
}
Wishlist hooks work just like cart hooks. Feel free to check how those work first.
The example below shows how to use the useWishlist
, useAddItem
and useRemoveItem
hooks:
import { useWishlist, useAddItem, useRemoveItem } from '@framework/wishlist'
const WishlistButton = ({ productId, variant }) => {
const addItem = useAddItem()
const removeItem = useRemoveItem()
const { data, isLoading, isEmpty, error } = useWishlist()
if (isLoading) return <p>Loading...</p>
if (error) return <p>{error.message}</p>
if (isEmpty) return <p>The wihslist is empty</p>
const { data: customer } = useCustomer()
const itemInWishlist = data?.items?.find(
(item) => item.product_id === productId && item.variant_id === variant.id
)
const handleWishlistChange = async (e) => {
e.preventDefault()
if (!customer) return
if (itemInWishlist) {
await removeItem({ id: itemInWishlist.id })
} else {
await addItem({
productId,
variantId: variant.id,
})
}
}
return (
<button onClick={handleWishlistChange}>
<Heart fill={itemInWishlist ? 'var(--pink)' : 'none'} />
</button>
)
}
While commerce hooks focus on client side data fetching and interactions, the commerce API focuses on static content generation for pages and API endpoints in a Node.js context.
The commerce API is currently going through a refactor in vercel/commerce#252 - We'll update the docs once the API is released.
Feel free to read through the source for more usage, and check the commerce vercel demo and commerce repo for usage examples: (demo.vercel.store) (repo)