- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Menu
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
Example: Show Product Variant's Sale Price
In this document, you'll learn how to display a product variant's sale price, with a full React example.
Check if a Price is a Sale#
To check if a product variant's price is a sale price, check whether the variant's calculated_price.calculated_price.price_list_type
field is equal to sale
:
Where selectedVariantPrice
is either the variant the customer selected or the cheapest variant.
Display Original and Discount Amounts#
If the price is a sale price, the original price is in the variant's calculated_price.original_amount
field:
1const salePrice = formatPrice(selectedVariantPrice.calculated_price.calculated_amount)2const originalPrice = formatPrice(selectedVariantPrice.calculated_price.original_amount)3const discountedAmount = formatPrice(4 selectedVariantPrice.calculated_price.original_amount - 5 selectedVariantPrice.calculated_price.calculated_amount6)
You can use the original price either to display it or calculate and display the discounted amount.
Full React Example#
For example, in a React-based storefront:
Note: The example only passes the
region_id
query parameter for pricing. Learn how to store and retrieve the customer's region in the Regions guides.1"use client" // include with Next.js 13+2 3import { useEffect, useMemo, useState } from "react"4import { HttpTypes } from "@medusajs/types"5import { useRegion } from "../providers/region"6 7type Props = {8 id: string9}10 11export default function Product({ id }: Props) {12 const [loading, setLoading] = useState(true)13 const [product, setProduct] = useState<14 HttpTypes.StoreProduct | undefined15 >()16 const [selectedOptions, setSelectedOptions] = useState<Record<string, string>>({})17 const { region } = useRegion()18 19 useEffect(() => {20 if (!loading) {21 return 22 }23 24 const queryParams = new URLSearchParams({25 fields: `*variants.calculated_price`,26 region_id: region.id,27 })28 29 fetch(`http://localhost:9000/store/products/${id}?${queryParams.toString()}`, {30 credentials: "include",31 headers: {32 "x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",33 },34 })35 .then((res) => res.json())36 .then(({ product: dataProduct }) => {37 setProduct(dataProduct)38 setLoading(false)39 })40 }, [loading])41 42 const selectedVariant = useMemo(() => {43 if (44 !product?.variants ||45 !product.options || 46 Object.keys(selectedOptions).length !== product.options?.length47 ) {48 return49 }50 51 return product.variants.find((variant) => variant.options?.every(52 (optionValue) => optionValue.value === selectedOptions[optionValue.option_id!]53 ))54 }, [selectedOptions, product])55 56 const formatPrice = (amount: number): string => {57 return new Intl.NumberFormat("en-US", {58 style: "currency",59 currency: region.currency_code,60 })61 .format(amount)62 }63 64 const selectedVariantPrice = useMemo(() => {65 if (selectedVariant) {66 return selectedVariant67 }68 69 return product?.variants?.sort((a: any, b: any) => {70 return (71 a.calculated_price.calculated_amount -72 b.calculated_price.calculated_amount73 )74 })[0]75 }, [selectedVariant, product])76 77 const price = useMemo(() => {78 if (!selectedVariantPrice) {79 return80 }81 82 // @ts-ignore83 return formatPrice(selectedVariantPrice.calculated_price.calculated_amount)84 }, [selectedVariantPrice])85 86 const isSale = useMemo(() => {87 if (!selectedVariantPrice) {88 return false89 }90 91 // @ts-ignore92 return selectedVariantPrice.calculated_price.calculated_price.price_list_type === "sale"93 }, [selectedVariantPrice])94 95 const originalPrice = useMemo(() => {96 if (!isSale) {97 return98 }99 100 // @ts-ignore101 return formatPrice(selectedVariantPrice.calculated_price.original_amount)102 }, [isSale, selectedVariantPrice])103 104 return (105 <div>106 {loading && <span>Loading...</span>}107 {product && (108 <>109 <h1>{product.title}</h1>110 {(product.options?.length || 0) > 0 && (111 <ul>112 {product.options!.map((option) => (113 <li key={option.id}>114 {option.title}115 {option.values?.map((optionValue) => (116 <button 117 key={optionValue.id}118 onClick={() => {119 setSelectedOptions((prev) => {120 return {121 ...prev,122 [option.id!]: optionValue.value!,123 }124 })125 }}126 >127 {optionValue.value}128 </button>129 ))}130 </li>131 ))}132 </ul>133 )}134 {selectedVariant && (135 <span>Selected Variant: {selectedVariant.id}</span>136 )}137 {price && (138 <span>139 {!selectedVariant && "From: "}140 {price}141 {isSale && `SALE - Original Price: ${originalPrice}`}142 </span>143 )}144 {product.images?.map((image) => (145 <img src={image.url} key={image.id} />146 ))}147 </>148 )}149 </div>150 )151}
In this example, you:
- Define an
isSale
memo variable that determines whether the chosen variant's price is a sale price. You do that by checking if the value of the variant'scalculated_price.calculated_price.price_list_type
field issale
. - Define an
originalPrice
memo variable that, ifisSale
is enabled, has the formatted original price of the chosen variant. The variant's original price is in thecalculated_price.original_amount
field. - If
isSale
is enabled, show a message to the customer indicating that this product is on sale along with the original price.
Was this page helpful?