Use Product Options in Storefront
In this guide, you'll learn how to list and retrieve product options in your storefront. You'll also learn how to use product options for filtering products.
What are Product Options?#
Product options are attributes like size or color that define the different variants of a product. For example, a t-shirt product may have a "Size" option with values like "S", "M", and "L", and a "Color" option with values like "Red", "Blue", and "Green".
Product options are useful for:
- Displaying filtering options in your storefront
- Allowing customers to select product variants based on their preferences
- Organizing product variants by their attributes
List Product Options#
To retrieve a list of product options, send a request to the List Product Options API route:
The response has a product_options field, which is an array of product options. Each product option has a values field, which is an array of the option's values.
Paginate Product Options#
To paginate product options, pass the following query parameters:
limit: The number of product options to return in the request.offset: The number of product options to skip before the returned options. You can calculate this by multiplying the current page with the limit.
The response object returns a count field, which is the total count of product options. Use it to determine whether there are more options that can be loaded.
For example:
1"use client" // include with Next.js 13+2 3import { useEffect, useState } from "react"4import { HttpTypes } from "@medusajs/types"5import { sdk } from "@/lib/sdk"6 7export default function ProductOptions() {8 const [loading, setLoading] = useState(true)9 const [options, setOptions] = useState<10 HttpTypes.StoreProductOption[]11 >([])12 const limit = 2013 const [currentPage, setCurrentPage] = useState(1)14 const [hasMorePages, setHasMorePages] = useState(false)15 16 useEffect(() => {17 if (!loading) {18 return 19 }20 21 const offset = (currentPage - 1) * limit22 23 sdk.store.productOption.list({24 limit,25 offset,26 })27 .then(({ product_options, count }) => {28 setOptions((prev) => {29 if (prev.length > offset) {30 // options already added because the same request has already been sent31 return prev32 }33 return [34 ...prev,35 ...product_options,36 ]37 })38 setHasMorePages(count > limit * currentPage)39 setLoading(false)40 })41 }, [loading])42 43 return (44 <div>45 {loading && <span>Loading...</span>}46 {!loading && options.length === 0 && <span>No options found.</span>}47 {!loading && options.length > 0 && (48 <ul>49 {options.map((option) => (50 <li key={option.id}>51 {option.title}52 <ul>53 {option.values?.map((value) => (54 <li key={value.id}>{value.value}</li>55 ))}56 </ul>57 </li>58 ))}59 </ul>60 )}61 {!loading && hasMorePages && (62 <button63 onClick={() => {64 setCurrentPage((prev) => prev + 1)65 setLoading(true)66 }}67 disabled={loading}68 >69 Load More70 </button>71 )}72 </div>73 )74}
In the example above, you add a useEffect hook that runs whenever the loading state changes. This hook fetches the product options, passing the limit and offset parameters to retrieve the paginated options.
You then show a button to load more options if there are more pages.
Retrieve a Product Option#
To retrieve a product option by its ID, send a request to the Retrieve Product Option API route:
The response has a product_option field, which is a product option object.
Use Product Options for Filtering#
Product options are commonly used to add filtering functionality to your storefront. You can display the available options and their values, then allow customers to filter products by selecting specific option values.
For example, you can create a ProductFilters component that retrieves options and allows selecting filters:
1"use client" // include with Next.js 13+2 3import { useEffect, useState } from "react"4import { HttpTypes } from "@medusajs/types"5import { sdk } from "@/lib/sdk"6 7type ProductFiltersProps = {8 selectedValues: string[]9 setSelectedValues: (values: string[]) => void10}11 12export default function ProductFilters({13 selectedValues,14 setSelectedValues,15}: ProductFiltersProps) {16 const [isLoading, setIsLoading] = useState(true)17 const [options, setOptions] = useState<HttpTypes.StoreProductOption[]>([])18 19 useEffect(() => {20 if (!isLoading) {21 return 22 }23 24 sdk.store.productOption.list()25 .then(({ product_options }) => {26 setOptions(product_options)27 setIsLoading(false)28 })29 }, [isLoading])30 31 return (32 <div>33 {isLoading && <span>Loading...</span>}34 {options.map((option) => (35 <div key={option.id}>36 <h4>{option.title}</h4>37 <ul>38 {option.values?.map((value) => (39 <li key={value.id}>40 <input41 type="checkbox"42 checked={selectedValues.includes(value.value)}43 onChange={(e) => {44 if (e.target.checked) {45 // add value46 setSelectedValues([47 ...selectedValues,48 value.value,49 ])50 } else {51 // remove value52 setSelectedValues(53 selectedValues.filter(54 (v) => v !== value.value55 )56 )57 }58 }}59 />60 <label>{value.value}</label>61 </li>62 ))}63 </ul>64 </div>65 ))}66 </div>67 )68}
The component receives the currently selected option values and a function to update them. When a user selects a filter, such as "Blue" for the "Color" option, the selected option values are updated.
Then, in the parent component that lists products, you can pass the selected option values to the option_value_id query parameter of the List Products API route:
The returned products are filtered to only include those whose variants have the selected option values.
option_value_id query parameter.Filter by Option ID#
Alternatively, you can filter products by the options they have using the option_id query parameter. This allows filtering products that have a specific option, such as "Size", regardless of the option value.
For example:
This returns all products that have the specified options, regardless of the option values.