Back to Medusa

{metadata.title}

www/apps/resources/app/storefront-development/cart/context/page.mdx

2.14.25.9 KB
Original Source

import { CodeTabs, CodeTab } from "docs-ui"

export const metadata = { title: Create Cart Context in Storefront, }

{metadata.title}

In this guide, you'll learn how to create a cart context in your storefront.

Why Create a Cart Context?

Throughout your storefront, you'll need to access the customer's cart to perform different actions. For example, you may need to add a product variant to the cart from the product page.

So, if your storefront is React-based, create a cart context and add it at the top of your components tree. Then, you can access the customer's cart anywhere in your storefront.


Create Cart Context Provider

For example, create the following file that exports a CartProvider component and a useCart hook:

<Note title="Tip"> </Note>

export const highlights = [ ["14", "cart", "Expose cart to children of the context provider."], ["15", "setCart", "Allow the context provider's children to update the cart."], ["18", "refreshCart", "Allow the context provider's children to unset and reset the cart."], ["27", "CartProvider", "The provider component to use in your component tree."], ["31", "useRegion", "Use the useRegion hook defined in the Region Context guide."], ["41", "create", "If the customer doesn't have a cart, create a new one."], ["50", "retrieve", "Retrieve the customer's cart."], ["57", "refreshCart", "This function unsets the cart, which triggers the useEffect callback to create a cart."], ["73", "useCart", "The hook that child components of the provider use to access the cart."] ]

tsx
"use client" // include with Next.js 13+

import { 
  createContext, 
  useContext, 
  useEffect, 
  useState,
} from "react"
import { HttpTypes } from "@medusajs/types"
import { useRegion } from "./region"
import { sdk } from "@/lib/sdk"

type CartContextType = {
  cart?: HttpTypes.StoreCart
  setCart: React.Dispatch<
    React.SetStateAction<HttpTypes.StoreCart | undefined>
  >
  refreshCart: () => void
}

const CartContext = createContext<CartContextType | null>(null)

type CartProviderProps = {
  children: React.ReactNode
}

export const CartProvider = ({ children }: CartProviderProps) => {
  const [cart, setCart] = useState<
    HttpTypes.StoreCart
  >()
  const { region } = useRegion()

  useEffect(() => {
    if (cart || !region) {
      return
    }

    const cartId = localStorage.getItem("cart_id")
    if (!cartId) {
      // create a cart
      sdk.store.cart.create({
        region_id: region.id,
      })
      .then(({ cart: dataCart }) => {
        localStorage.setItem("cart_id", dataCart.id)
        setCart(dataCart)
      })
    } else {
      // retrieve cart
      sdk.store.cart.retrieve(cartId)
      .then(({ cart: dataCart }) => {
        setCart(dataCart)
      })
    }
  }, [cart, region])

  const refreshCart = () => {
    localStorage.removeItem("cart_id")
    setCart(undefined)
  }

  return (
    <CartContext.Provider value={{
      cart,
      setCart,
      refreshCart,
    }}>
      {children}
    </CartContext.Provider>
  )
}

export const useCart = () => {
  const context = useContext(CartContext)

  if (!context) {
    throw new Error("useCart must be used within a CartProvider")
  }

  return context
}

The CartProvider handles retrieving or creating the customer's cart. It uses the useRegion hook defined in the Region Context guide to associate the cart with the customer's selected region.

The useCart hook returns the value of the CartContext. Child components of CartProvider use this hook to access cart, setCart, or refreshCart.

refreshCart unsets the cart, which triggers the useEffect callback to create a cart. This is useful when the customer logs in or out, or after the customer places an order.

<Note title="Tip">

You can add to the context and provider other functions useful for updating the cart and its items. Refer to the following guides for details on how to implement these functions:

</Note>

Use CartProvider in Component Tree

To use the cart context's value, add the CartProvider high in your component tree.

For example, if you're using Next.js, add it to the app/layout.tsx or src/app/layout.tsx file:

tsx
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import "./globals.css"
import { CartProvider } from "@/providers/cart"
import { RegionProvider } from "@/providers/region"

const inter = Inter({ subsets: ["latin"] })

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <RegionProvider>
          <CartProvider>
            {children}
          </CartProvider>
        </RegionProvider>
      </body>
    </html>
  )
}

Make sure to put the CartProvider as a child of the RegionProvider since it uses the useRegion hook defined in the Region Context guide.

Use useCart Hook

Now, you can use the useCart hook in child components of CartProvider.

For example:

tsx
"use client" // include with Next.js 13+
// ...
import { useCart } from "@/providers/cart"

export default function Products() {
  const { cart } = useCart()
  // ...
}

The useCart hook returns the cart details, which you can use in your components.