Example: Show Product Variant's Price
In this guide, you'll learn how to display a product variant's price with a full React example.
This guide doesn't take into account sale or tax prices. Refer to the following guides to learn how to show them:
Price Formatting#
To format a price, use JavaScript's NumberFormat utility. You pass it the amount and the currency code (which you retrieve from the selected region):
const formatPrice = (amount: number): string => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: region.currency_code,
})
.format(amount)
}
You'll use this function to format prices in your storefront, including the selected variant's price.
Display Selected Variant Price#
Once the customer selects a variant (as explained in the Select Product Variants guide), use its calculated_price.calculated_amount property to display its price:
The price variable holds the formatted price of the selected variant with the currency of the selected region.
Full React Example#
The following React-based storefront example retrieves the product's price based on the selected variant:
- This example uses the
useRegionhook defined in the Region React Context guide to retrieve the selected region's currency code. - Learn how to install and configure the JS SDK in the JS SDK documentation.
"use client" // include with Next.js 13+
import { useEffect, useMemo, useState } from "react"
import { HttpTypes } from "@medusajs/types"
import { useRegion } from "@/providers/region"
import { sdk } from "@/lib/sdk"
type Props = {
id: string
}
export default function Product({ id }: Props) {
const [loading, setLoading] = useState(true)
const [product, setProduct] = useState<
HttpTypes.StoreProduct | undefined
>()
const [selectedOptions, setSelectedOptions] = useState<Record<string, string>>({})
const { region } = useRegion()
useEffect(() => {
if (!loading) {
return
}
sdk.store.product.retrieve(id, {
fields: `*variants.calculated_price`,
region_id: region.id,
})
.then(({ product: dataProduct }) => {
setProduct(dataProduct)
setLoading(false)
})
}, [loading])
const selectedVariant = useMemo(() => {
if (
!product?.variants ||
!product.options ||
Object.keys(selectedOptions).length !== product.options?.length
) {
return
}
return product.variants.find((variant) => variant.options?.every(
(optionValue) => optionValue.value === selectedOptions[optionValue.option_id!]
))
}, [selectedOptions, product])
const formatPrice = (amount: number): string => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: region.currency_code,
})
.format(amount)
}
const selectedVariantPrice = useMemo(() => {
if (selectedVariant) {
return selectedVariant
}
return product?.variants?.sort((a: any, b: any) => {
return (
a.calculated_price.calculated_amount -
b.calculated_price.calculated_amount
)
})[0]
}, [selectedVariant, product])
const price = useMemo(() => {
if (!selectedVariantPrice) {
return
}
// @ts-ignore
return formatPrice(selectedVariantPrice.calculated_price.calculated_amount)
}, [selectedVariantPrice])
return (
<div>
{loading && <span>Loading...</span>}
{product && (
<>
<h1>{product.title}</h1>
{(product.options?.length || 0) > 0 && (
<ul>
{product.options!.map((option) => (
<li key={option.id}>
{option.title}
{option.values?.map((optionValue) => (
<button
key={optionValue.id}
onClick={() => {
setSelectedOptions((prev) => {
return {
...prev,
[option.id!]: optionValue.value!,
}
})
}}
>
{optionValue.value}
</button>
))}
</li>
))}
</ul>
)}
{selectedVariant && (
<span>Selected Variant: {selectedVariant.id}</span>
)}
{price && (
<span>
{!selectedVariant && "From: "}
{price}
</span>
)}
{product.images?.map((image) => (
<img src={image.url} key={image.id} />
))}
</>
)}
</div>
)
}
In the example above, you:
- Use the
useRegionhook defined in the previous Region React Context guide to retrieve the selected region's currency code. This is necessary to format the variant's price. - Pass the pricing query parameters to the request retrieving the product. This retrieves for every variant a new
calculated_pricefield holding details about the variant's price.- You can pass other pricing query parameters for more accurate pricing. Refer to the Retrieve Product Variant's Prices guide for more information.
- Choose the variant to show its price:
- If there's a selected variant, choose it.
- If there isn't a selected variant, retrieve and choose the variant with the cheapest price.
- Format the price based on the chosen variant in the previous step. The variant's
calculated_price.calculated_amountfield is used. - Display the formatted price to the customer. If there isn't a select variant, show a
Fromlabel to indicate that the price shown is the cheapest.