🚧

Important

Make sure to adjust the examples below accordingly to your project and store's platform.

Read more about platforms considerations here.

Creating a AddToCartButton component

import React from 'react'
import { useCartState, useCartActions } from 'frontend-checkout'
import LoadingSpinner from 'Components/LoadingSpinner'

import './styles.css'

// Button states
const IDLE = 'idle'
const LOADING = 'loading'
const SOLD_OUT = 'sold out'
const ERROR = 'error'

// Error message duration
const THREE_SECONDS = 3 * 1000

const AddToCart = ({ id, children }) => {
  const [buttonState, setButtonState] = React.useState(IDLE)

  const { inventory } = useCartState()
  const { addItems, isProductAvailableForSale } = useCartActions()

  React.useEffect(
    function setSoldOutStateIfItemIsNotAvailable() {
      if (inventory.status === LOADING || inventory.status === ERROR) return

      const availableForSale = isProductAvailableForSale({
        id: variantId,
        type: 'ProductVariant',
      })

      if (!availableForSale) setButtonState(SOLD_OUT)
    },
    [inventory.status],
  )

  function clearError() {
    setButtonState(IDLE)
  }

  async function handleAddItemToCart() {
    setButtonState(LOADING)
    try {
      await addItems({ id, quantity: 1 })
      setButtonState(IDLE)
    } catch (e) {
      setButtonState(ERROR)
      setTimeout(clearError, THREE_SECONDS) // Remove error message after 3 seconds.
    }
  }

  if (buttonState === LOADING)
    return (
      <button className="AddToCart">
        <LoadingSpinner />
      </button>
    )

  if (buttonState === SOLD_OUT)
    return (
      <button className="AddToCart AddToCart--sold" disabled>
        Sold out
      </button>
    )

  if (buttonState === ERROR)
    return (
      <button className="AddToCart" onClick={clearError}>
        Adding failed
      </button>
    )

  return (
    <button className="AddToCart" onClick={handleAddItemToCart}>
      {children}
    </button>
  )
}

export default AddToCart

Creating a CheckoutLink component

import React from 'react'
import { useCartState } from 'frontend-checkout'

import './styles.css'

const CheckoutLink = ({ children }) => {
  const { checkoutUrl } = useCartState()

  return (
    <a className="CheckoutLink" href={checkoutUrl}>
      {children}
    </a>
  )
}

export default CheckoutLink

Creating a ItemQuantityChange component

import React from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const IDLE = 'idle'
const LOADING = 'loading'

const ItemQuantityChange = ({ id, initialQuantity = 0 }) => {
  const [inputStatus, setInputStatus] = React.useState(IDLE)
  const { updateItems, removeItems } = useCartActions()

  async function handleItemQuantityChange(requestedQtyChange) {
    const newQuantity = quantity + requestedQtyChange

    if (newQuantity === quantity) return

    setInputStatus(LOADING)
    try {
      if (newQuantity === 0) {
        await removeItems(id)
      } else {
        await updateItems({ id, quantity: newQuantity })
        setInputStatus(IDLE)
      }
    } catch (e) {
      setInputValue(quantity)
      setInputStatus(IDLE)
      // Show error message
    }
  }

  return (
    <React.Fragment>
      <button
        className="ItemQuantityInput-btn--minus"
        disabled={inputStatus === LOADING}
        onClick={() => handleItemQuantityChange(-1)}
      >
        Reduce item quantity by one
      </button>
      <label htmlFor="itemQty">Item quantity</label>
      <input
        id="itemQty"
        className="ItemQuantityInput-input"
        type="number"
        min={0}
        disabled={inputStatus === LOADING}
        onBlur={(e) =>
          handleItemQuantityChange(
            Number(e.target.value) - quantity /* difference between new and current quantity */,
          )
        }
      />
      <button
        className="ItemQuantityInput-btn--plus"
        disabled={inputStatus === LOADING}
        onClick={() => handleItemQuantityChange(1)}
      >
        Increase item quantity by one
      </button>
    </React.Fragment>
  )
}

export default ItemQuantityChange

Creating a change item variant component

import React from 'react'
import { useCartState } from 'frontend-checkout'

import './styles.css'

const CartPage = () => {
  const { items } = useCartState()

  return (
    <CartContainer>
      {items.map((item) => (
        <CartItem key={item.id} item={item} />
      ))}
    </CartContainer>
  )
}

export default CartPage
import React, { useEffect, useState } from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const CartItem = ({ item }) => {
  const { fetchProduct } = useCartActions()
  const [variants, setVariants] = useState([])

  // Shopify Storefront API (GraphQL)
  const productId = item.variant.product.id // product id must be used in GraphQL API (Variant and CheckoutLineItem id will not work)

  // Shopify AJAX API (REST)
  const productId = item.handle // product handle must be used in REST API

  useEffect(
    async function getProductVariants() {
      const product = await fetchProduct(productId)

      if (product == null) return

      const { variants } = product

      setVariants(variants)
    },
    [productId, fetchProduct, setVariants],
  )

  return (
    <CartItemContainer>
      <CartItemTitle item={item} />
      <ItemQuantity item={item} />
      <VariantsDropdown itemId={item.id} quantity={item.quantity} variants={variants} />
    </CartItemContainer>
  )
}

export default CartItem
import React, { useEffect, useState } from 'react'
import { useCartActions } from 'frontend-checkout'

import './styles.css'

const VariantsDropdown = ({ itemId, quantity, variants }) => {
  const { addItems, removeItems } = useCartActions()

  async function handleVariantChange(variant) {
    // we can't update existing item in cart
    // we need to remove and then add new variant
    await removeItems(itemId)

    await addItems({ id: variant.id, quantity })
  }

  return (
    <VariantsDropdownContainer>
      {variants.map((variant) => (
        <VariantsDropdownItem variant={variant} onSelect={handleVariantChange} />
      ))}
    </VariantsDropdownContainer>
  )
}

export default VariantsDropdown

Updating cart attribute

import React from 'react'
import { useCartState, useCartActions } from 'frontend-checkout'

import './styles.css'

const CartPage = () => {
  const { updateCart } = useCartActions()
  const { attributes } = useCartState()

  async function addTestAttrToCart() {
    // updateCart({ attributes: {}, note: '', items: [] })
    // this method is able to add/update cart attributes, note as well
    // as item quantities (all parameters are optional).
    await updateCart({ attributes: { test: 'test' } })
  }

  return (
    <CartContainer>
      <button onClick={addTestAttrToCart}>Add Test Attr</button>
        {Object.keys(attributes).map(attributesKey => {
          return <p key={someRandomId}>{attributesKey}: {attributes[attributesKey]}</p>
        })}
    </CartContainer>
  )
}

export default CartPage