- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Menu
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Retrieve Product Variant's Inventory in Storefront
To retrieve variants' inventory quantity using either the List Products or Retrieve Products API routes:
- Pass the publishable API key in the header of the request. The retrieved inventory quantity is in the locations associated with the key's sales channels.
- Pass in the
fields
query parameter the value+variants.inventory_quantity
.
For example:
1const queryParams = new URLSearchParams({2 fields: `*variants.calculated_price,+variants.inventory_quantity`,3})4 5fetch(`http://localhost:9000/store/products/${id}?${queryParams.toString()}`, {6 credentials: "include",7 headers: {8 "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",9 },10})11.then((res) => res.json())12.then(({ product }) => {13 product.variants?.forEach((variant) => {14 const isInStock = variant.manage_inventory === false || 15 variant.inventory_quantity > 016 17 // ...18 })19})
Important: If you're also passing
*variants.calculated_price
in fields
to get the product variants' prices, make sure to include it in the beginning of the list of fields. For example, ?fields=*variants.calculated_price,+variants.inventory_quantity
.When is a Variant in Stock?#
A variant is in stock if:
- Its
manage_inventory
's value isfalse
, meaning that Medusa doesn't keep track of its inventory. - If its
inventory_quantity
's value is greater than0
. This property is only available on variants whosemanage_inventory
isfalse
.
Full React Example#
1"use client" // include with Next.js 13+2 3import { useEffect, useMemo, useState } from "react"4import { HttpTypes } from "@medusajs/types"5 6type Props = {7 id: string8}9 10export default function Product({ id }: Props) {11 const [loading, setLoading] = useState(true)12 const [product, setProduct] = useState<13 HttpTypes.StoreProduct | undefined14 >()15 const [selectedOptions, setSelectedOptions] = useState<Record<string, string>>({})16 17 useEffect(() => {18 if (!loading) {19 return 20 }21 22 const queryParams = new URLSearchParams({23 fields: `+variants.inventory_quantity`,24 })25 26 fetch(`http://localhost:9000/store/products/${id}?${queryParams.toString()}`, {27 credentials: "include",28 headers: {29 "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",30 },31 })32 .then((res) => res.json())33 .then(({ product: dataProduct }) => {34 setProduct(dataProduct)35 setLoading(false)36 })37 }, [loading])38 39 const selectedVariant = useMemo(() => {40 if (41 !product?.variants ||42 !product.options || 43 Object.keys(selectedOptions).length !== product.options?.length44 ) {45 return46 }47 48 return product.variants.find((variant) => variant.options?.every(49 (optionValue) => optionValue.value === selectedOptions[optionValue.option_id!]50 ))51 }, [selectedOptions, product])52 53 const isInStock = useMemo(() => {54 if (!selectedVariant) {55 return undefined56 }57 58 return selectedVariant.manage_inventory === false || selectedVariant.inventory_quantity > 059 }, [selectedVariant])60 61 return (62 <div>63 {loading && <span>Loading...</span>}64 {product && (65 <>66 <h1>{product.title}</h1>67 {(product.options?.length || 0) > 0 && (68 <ul>69 {product.options!.map((option) => (70 <li key={option.id}>71 {option.title}72 {option.values?.map((optionValue) => (73 <button 74 key={optionValue.id}75 onClick={() => {76 setSelectedOptions((prev) => {77 return {78 ...prev,79 [option.id!]: optionValue.value!,80 }81 })82 }}83 >84 {optionValue.value}85 </button>86 ))}87 </li>88 ))}89 </ul>90 )}91 {selectedVariant && (92 <span>Selected Variant: {selectedVariant.id}</span>93 )}94 {isInStock !== undefined && (95 <span>96 {isInStock && "In Stock"}97 {!isInStock && "Out of Stock"}98 </span>99 )}100 </>101 )}102 </div>103 )104}
In this example, you show whether the selected variant is in or out of stock.
Was this page helpful?