Shopify

Frontend Checkout

useCartState

The useCartState hook can be used to get the current state of the user's cart.

🚧

The data returned by useCartState is different than the CMS data.

import { useCartState } from 'frontend-checkout' 

const cart = useCartState()

// Cart
{
 id: string // ID of current cart.
 items: Items[] // Array of items currently in cart. Check `Items` type below.
 // 🚨 `inventory` is to be deprecated 🚨
 // Use https://docs.getshogun.com/shogun-frontend-guides/docs/frontend-checkout-migrate-to-inventory-actions
 // or https://docs.getshogun.com/shogun-frontend-guides/docs/frontend-checkout-shopify#useinventory
 inventory: {
   products: Record<productId, {
     availableForSale: boolean  // Indicates should you allow purchasing of a product, e.g. out of stock.
     quantity: number // The available quantity of a given product, if allowed on store.
     // If you want to use this feature, please contact Shogun support.
     minOrder?: number // Minimum order constraint, adjustable in Shogun CMS - Product Content Group.
     // If you want to use this feature, please contact Shogun support.
     maxOrder?: number // Maximum order constraint.
   }
   productVariants: Record<variantId, {
     availableForSale: boolean
     quantity: number
     // If you want to use this feature, please contact Shogun support.
     minOrder?: number
     // If you want to use this feature, please contact Shogun support.
     maxOrder?: number
   }
   status: 'loading' | 'loaded' | 'error' // Status of loading products from CMS
 }
 subtotalPrice: number // Total price of all items in cart, before shipping, taxes, and discounts.
 currencyCode: string // Cart currency code, e.g. "USD".Depends on selected country.
 countryCode: string // Cart country code, e.g. "US". Persisted between session.
 isCartShown: boolean // Flag for managing should cart modal or drawer be visible.
 checkoutUrl: string // Url to redirect users when they press `Checkout` link/button.
 attributes: object
 note?: string
 currencies?: string[]
}

// Items
{
  variant_id: string
  selectedCurrencyPrice: string
}

Items type

// Items type
interface ShopifyStorefrontLineItem {
  id: string | number
  quantity: number
  title: string
  variant?: ShopifyStorefrontProductVariantWithProduct | null
  customAttributes: { key: string; value: string }[]
  discountAllocations: {
    allocatedAmount: MoneyV2
    discountApplication: DiscountApplication
  }[]
  onlineStoreUrl?: string
  attrs?: any
}

interface DiscountApplication {
  targetSelection: 'ALL' | 'ENTITLED' | 'EXPLICIT'
  allocationMethod: 'ACROSS' | 'EACH'
  targetType: 'LINE_ITEM' | 'SHIPPING_LINE'
  value: MoneyV2 | { percentage: number }
  title: string | null | undefined
  description: string | null | undefined
  code: string | null | undefined
  applicable: string | null | undefined
}

interface ShopifyStorefrontProductVariantWithProduct
  extends ShopifyStorefrontProductVariant {
  product: {
    id: string
    handle: string
  }
}

interface MoneyV2 {
  amount: string
  currencyCode: string
}

type MeasuredType = 'VOLUME' | 'WEIGHT' | 'LENGTH' | 'AREA'

type UnitOfMeasurement =
  | 'ML'
  | 'CL'
  | 'L'
  | 'M3'
  | 'MG'
  | 'G'
  | 'KG'
  | 'MM'
  | 'CM'
  | 'M'
  | 'M2'

interface ShopifyStorefrontProductVariant {
  id: string | number
  title: string
  price: string
  priceV2: MoneyV2
  weight: number
  availableForSale: boolean
  sku: string
  compareAtPrice: string
  compareAtPriceV2: MoneyV2
  image: Image
  selectedOptions: {
    name: string
    value: string
  }[]
  unitPrice: MoneyV2
  unitPriceMeasurement: {
    measuredType: MeasuredType | null
    quantityUnit: UnitOfMeasurement | null
    quantityValue: number
    referenceUnit: UnitOfMeasurement | null
    referenceValue: number
  }
}
// Items type
interface ShopifyAjaxLineItem {
  id: number
  discounted_price: number
  discounts: unknown[]
  featured_image: { aspect_ratio: number; alt: string; height: number; url: string; width: number }
  final_line_price: number
  final_price: number
  gift_card: boolean
  grams: number
  handle: string
  image: string
  key: string
  line_level_discount_allocations: unknown[]
  line_level_total_discount: number
  line_price: number
  options_with_values: { name: string; value: string }[]
  original_line_price: number
  original_price: number
  price: number
  product_description: string
  product_has_only_default_variant: boolean
  product_id: number
  product_title: string
  product_type: string
  properties: Record<string, string> | null
  quantity: number
  requires_shipping: boolean
  sku: string | null
  taxable: boolean
  title: string
  total_discount: number
  url: string
  variant_id: number
  variant_options: string[]
  variant_title: string
  vendor: string
}

useCartActions

The useCartActions hook can be used to get actions available for manipulating the cart.

import { useCartActions } from 'frontend-checkout'

fetchProduct

/**
 * Fetch a single product by its ID.
 * fetchProduct: (id: string) => Promise<Product>
 */
const { fetchProduct } = useCartActions()

await fetchProduct('Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=')
/**
 * fetchProduct: (id: string) => Promise<Product>
 */
const { fetchProduct } = useCartActions()

await fetchProduct('product-handle')

addItems

/**
 * Add items to cart.
 * addItems: (items: Item | Item[]) => Promise<Cart>
 */
const { addItems } = useCartActions()

/**
 * Item: {
 *  id: string | number
 *  quantity: number
 *  [key: string]?: any
 */
const item = {
  // variant id, in Shogun Frontend CMS that is `product.variant.storefrontId`
  id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=',
  quantity: 1,
  // optional
  customAttributes: [{key: 'MyKey', value: 'MyValue'}],
}

// To add a single item to the cart:
await addItems(item)

// To add multiple items to the cart:
await addItems([item, anotherItem])
/**
 * Add items to cart.
 * addItems: (items: Item | Item[]) => Promise<Cart>
 */
const { addItems } = useCartActions()

/**
 * Item: {
 *  id: string | number
 *  quantity: number
 *  [key: string]?: any
 */
const item = {
  // variant id, in Shogun Frontend CMS that is `product.variant.externalId`
  id: 7009655818753,
  quantity: 1,
  // optional
  properties: {key: 'value'},
}

// To add a single item to the cart:
await addItems(item)

// To add multiple items to the cart:
await addItems([item, anotherItem])

updateItems

/**
 * Update items in cart.
 * updateItems: (items: Item | Item[]) => Promise<Cart>
 */
const { updateItems } = useCartActions()

const { items } = useCartState()

/**
 * Item: {
 *  id: string | number
 *  quantity: number
 *  [key: string]?: any
 */
const item = {
  // id of the item in the cart that you want to update
  id: items[0].id,
  // change the quantity
  quantity: 2,
}

// To update a single item in the cart:
await updateItems(item)

// To update multiple items in the cart:
await updateItems([item, anotherItem])
/**
 * Update items in cart.
 * updateItems: (items: Item | Item[]) => Promise<Cart>
 */
const { updateItems } = useCartActions()

const { items } = useCartState()

/**
 * Item: {
 *  id: string | number
 *  quantity: number
 *  [key: string]?: any
 */
const item = {
  // id of the item in the cart that you want to update
  // Iff you use a 'variant_id' that is not in the cart,
  // then a new item will be added to cart.
  // If there are multiple items in cart with the same 'variant_id'
  // only the first one will be updated.
  id: items[0].variant_id,
  // change the quantity
  quantity: 2,
}

// If there are multiple items in the cart with the same 'variant_id'
// and you need to update/change one that is not first,
// instead of 'id' you should provide 'line' property
// which is 1-based index position of the item in the cart.
const item = {
  // update second item in the cart
  line: 2,
  // change the quantity (quantity will be adjusted to maximum items in stock)
  quantity: 2,
}

// To update a single item in the cart:
await updateItems(item)

// To update multiple items in the cart:
await updateItems([item, anotherItem])

removeItems

/**
 * Remove items from cart.
 * removeItems: (itemIds: string | string[]) => Promise<Cart>
 */
const { removeItems } = useCartActions()

const { items } = useCartState()

const itemId = items[0].id // remove first item in cart

// To remove a single item from the cart:
await removeItems(itemId)

// To remove multiple items from the cart:
await removeItems([itemid, anotherItemId])
/**
 * Remove items from cart.
 * removeItems: (itemIds: string | string[]) => Promise<Cart>
 */
const { removeItems } = useCartActions()

const { items } = useCartState()

const itemId = items[0].variant_id // remove first item in cart

// To remove a single item from the cart:
await removeItems(itemId)

// To remove multiple items from the cart:
await removeItems([itemid, anotherItemId])

isProductInInventory

❗️

TO BE DEPRECATED

Use isProductAvailableForSale instead.

/**
 * Check if product is in the inventory.
 * isProductInInventory: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => boolean
 */
const { isProductInInventory } = useCartActions()

// id is the Product Variant storefrontId
isProductInInventory({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Check if product is in the inventory.
 * isProductInInventory: ({ id: ItemId, type: ProductType = defaultProductType }) => boolean
 */
const { isProductInInventory } = useCartActions()

// id is the Product Variant externalId
isProductInInventory({ id: '6690750136476=' })

isProductAvailableForSale

/**
 * Check if product is available for sale from the inventory.
 * isProductAvailableForSale: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => boolean
 */
const { isProductAvailableForSale } = useCartActions()

// id is the Product Variant storefrontId
isProductAvailableForSale({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Get item is available for sale from inventory.
 * isProductAvailableForSale: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => boolean
 */
const { isProductAvailableForSale } = useCartActions()

// id is the Product Variant externalId
isProductAvailableForSale({ id: 6690750136476 })

getProductQuantity

/**
 * Get product quantity from the inventory.
 * getProductQuantity: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number
 */
const { getProductQuantity } = useCartActions()

// id is the Product Variant storefrontId
getProductQuantity({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Get product quantity from the inventory.
 * getProductQuantity: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number
 */
const { getProductQuantity } = useCartActions()

// id is the Product Variant externalId
getProductQuantity({ id: 6690750136476 })

getProductMinOrder

🚧

If you want to use this feature, please contact Shogun support.

getProductMinOrder is not enabled by default because it increases the store's build time.

/**
 * Get product minimum order from the inventory.
 * getProductMinOrder: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number | undefined
 */
const { getProductMinOrder } = useCartActions()

// id is the Product Variant storefrontId
getProductMinOrder({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Get product minimum order from the inventory.
 * getProductMinOrder: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number | undefined
 */
const { getProductMinOrder } = useCartActions()

// id is the Product Variant externalId
getProductMinOrder({ id: 6690750136476 })

getProductMaxOrder

🚧

If you want to use this feature, please contact Shogun support.

getProductMaxOrder is not enabled by default because it increases the store's build time.

/**
 * Get product maximum order from the inventory.
 * getProductMaxOrder: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number | undefined
 */
const { getProductMaxOrder } = useCartActions()

// id is the Product Variant storefrontId
getProductMaxOrder({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Get product maximum order from the inventory.
 * getProductMaxOrder: ({ id: ItemId, type: ProductType = 'ProductVariant' }) => number | undefined
 */
const { getProductMaxOrder } = useCartActions()

// id is the Product Variant externalId
getProductMaxOrder({ id: 6690750136476 })

getProductPrice

/**
 * Get product price.
 * getProductPrice: ({ id: string | number }) => string | number | undefined
 */
const { getProductPrice } = useCartActions()

// id is the Product Variant storefrontId
await getProductPrice({ id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' })
/**
 * Get product price.
 * getProductPrice: ({ id: string | number }) => string | number | undefined
 */
const { getProductPrice } = useCartActions()

// id is the Product Variant externalId
await getProductPrice({ id: 6690750136476 })

getCurrencies

🚧

Available only on Shopify Storefront API (GraphQL).

/**
 * Get all currencies enabled for your storefront.
 * getCurrencies: () => string[]
 */
const { getCurrencies } = useCartActions()

await getCurrencies()

selectCurrency

🚧

Deprecated in '[email protected]'

selectCurrency works only on frontend-checkout version below 3.6.0. Version 3.6.0 introduced a new approach to handling international pricing based on Shopify Markets.

📘

Make sure your Shopify store is not using any rules to change prices. To check the rules, go to Settings > Markets > {country name}.

/**
 * Select currency.
 * type LineItem = {
 *   quantity: number
 *   variantId: string | number
 *   customAttributes: {
 *     key: string
 *     value: string
 *   }[]
 * }
 * selectCurrency: ({ currency, lineItems }: { currency: string, lineItems: LineItem[] }) => Promise<Cart>
 */
const lineItems = items.map(({ quantity, variant, customAttributes }) => ({
  quantity,
  variantId: variant.id,
  customAttributes,
}))

await selectCurrency({
  currency: 'EUR', // one of available currencies
  lineItems,
})

selectCountry

🚧

Available only on Shopify Storefront API (GraphQL).

📘

Make sure your Shopify store is not using any rules to change prices. To check the rules, go to Settings > Markets > {country name}.

📘

selectCurrency call will create a new cart using a different currency. Currency is chosen based on the provided country by Shopify.

/**
 * Select country.
 * selectCurrency: selectCountry: (country: string) => Promise<Cart>
 */
await selectCountry('US')
})

updateCart

🚧

Available only on Shopify AJAX API (REST).

/**
 * Update cart attributes, notes and items quantities
 * updateCart({ attributes?: Record<string, string>, note: string | null, items?: Item[] }) => Promise<Cart>
 */
const { updateCart } = useCartActions()

await updateCart({
  attributes: { something: 'else' },
  note: 'Test note',
})

showCart

/**
 * Show cart.
 * showCart: () => void
 */
const { showCart } = useCartActions()

showCart() // isCartShown from useCartState will become true.

hideCart

/**
 * Hide cart.
 * hideCart: () => void
 */
const { hideCart } = useCartActions()

hideCart() // isCartShown from useCartState will become false.

useInventory

The useInventory hook can be used to retrieve inventory data on demand.

/**
 * Retrieve inventory data directly from the platform API
 * This hook accepts an array of IDs
 * The hook will retrieve the inventory data on the given product IDs
 * Optionally, an interval (in milliseconds)  can be passed to automatically refresh the data after the given interval
 * useInventory: (props: { ids: string[] | number[], inverval?: number, productType?: 'Product' | 'ProductVariant' }) => {
 *   products: Record<id, { availableForSale: boolean, quantity: number, minOrder: number, maxOrder: number, price: number }>
 *   status: 'initial' | 'loading' | 'loaded' | 'error'
 * }
 */
import { useInventory } from 'frontend-checkout'

const id = 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=' // storefrontId

// By passing an id and product type, useInventory will fetch the data for given ids
// Note that ids will always expect an array of IDs whether you wish to retrieve the data
// For a single product or multiple products.

// To retrieve data for a single product, pass the ID as a single element array
const { products, status } = useInventory({ ids: [id], productType: 'Product' })

// By passing an id and product type, useInventory will fetch the data for given ids
const { products, status } = useInventory({ ids: [id], productType: 'Product' })
// minOrder and maxOrder comes from Shogun Frontend CMS and
// it will only be updated after building and publishing the store
const { availableForSale, quantity, minOrder, maxOrder, price } = products[id] || {}

// To retrieve the inventory data for multiple products, pass the IDs as an array
// Note that in this case you must ensure that all IDs are of the same product type
// When passing multiple IDs, they cannot be of a mixed product type
// Retrieving the inventory data for an array of products can be useful when used in collection pages
const ids = ['Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzODQ=', 'W2mkOi8vc2hvcGlmeS9Qcm9kdWN0Lzc4NTc5ODkzOKX=']
const { products, status } = useInventory({ ids, productType: 'Product' })

ids.forEach(productKey => {
  const { availableForSale, quantity, minOrder, maxOrder, price } = products[productKey] || {}
})

// A custom interval (in milliseconds) can be passed
const { products, status } = useInventory({ ids: [ id ], productType: 'Product', interval: 5000 })
const { availableForSale, quantity, minOrder, maxOrder, price } = products[id] || {}
/**
 * Retrieve inventory data directly from the platform API
 * This hook accepts an array of IDs
 * The hook will retrieve the inventory data on the given product IDs
 * Optionally, an interval (in milliseconds) can be passed to automatically refresh the data after the given interval
 * useInventory: (props: { ids: string[] | number[], inverval?: number, productType?: 'Product' | 'ProductVariant' }) => {
 *   products: Record<id, { availableForSale: boolean, quantity: number, minOrder: number, maxOrder: number, price: number }>
 *   status: 'initial' | 'loading' | 'loaded' | 'error'
 * }
 */
import { useInventory } from 'frontend-checkout'

const id = 6690750136476 // externalId

// By passing an id and product type, useInventory will fetch the data for given ids
// Note that ids will always expect an array of IDs whether you wish to retrieve the data
// For a single product or multiple products.

// To retrieve data for a single product, pass the ID as a single element array
// productType defaults to "Product"
const { products, status } = useInventory({ ids: [id] })

// By passing an id and product type, useInventory will fetch the data for given ids
const { products, status } = useInventory({ ids: [id], productType: 'Product' })
const { availableForSale, quantity, minOrder, maxOrder, price } = products[id] || {}

// To retrieve the inventory data for multiple products, pass the IDs as an array
// Note that in this case you must ensure that all IDs are of the same product type
// When passing multiple IDs, they cannot be of a mixed product type
// Retrieving the inventory data for an array of products can be useful when used in collection pages
const ids = ['6690750136476', '6660050136477']
const { products, status } = useInventory({ ids, productType: 'Product' })

ids.forEach(productKey => {
  const { availableForSale, quantity, minOrder, maxOrder, price } = products[productKey] || {}
})

// A custom interval (in milliseconds) can be passed
const { products, status } = useInventory({ ids: [ id ], productType: 'Product', interval: 5000 })
const { availableForSale, quantity, minOrder, maxOrder, price } = products[id] || {}