⚙️ getProduct()
Options
The getProduct()
function accepts a single argument of type GetProductOptions
. It lets you control how the product is fetched, what extra data is included, and how that data is shaped.
🧾 Input Shape
interface GetProductOptions {
id?: string;
handle?: string;
customMetafields?: CustomMetafieldDefinition[];
variantMetafields?: CustomMetafieldDefinition[];
options: {
locale?: string;
resolveFiles?: boolean;
renderRichTextAsHtml?: boolean;
camelizeKeys?: boolean;
transformMetafields?: (raw, casted, definitions) => Record<string, any>;
transformVariantMetafields?: (raw, casted, definitions) => Record<string, any>;
};
}
🔑 id
or handle
id?: string
handle?: string
- Use one or the other to identify the product to fetch.
handle
is recommended for URL-based routing.- If neither is provided, the function will return an error.
🧩 customMetafields
customMetafields?: CustomMetafieldDefinition[]
Use this to fetch product-level metafields, casted and typed for use in your frontend.
Each metafield definition looks like:
{ field: "custom.warranty", type: "single_line_text" }
You can define multiple metafields here and retrieve them using the metafields
key on the returned product.
➡️ See Custom Metafields →
🔀 variantMetafields
variantMetafields?: CustomMetafieldDefinition[]
Similar to customMetafields
, but applied to each product variant.
This is perfect for cases like:
- Variant-specific sizing, weight, or specs
- Conditional rendering of swatches or details
Returned in each variant object under metafields
.
⚙️ options
The options
object lets you customize how the returned data is parsed and transformed.
📍 locale
locale?: string
Shopify's storefront API supports localization — pass the desired locale like "fr"
or "en"
to retrieve translated content (if available).
🖼️ resolveFiles
resolveFiles?: boolean // default: true
When true
, file metafields will be resolved and returned as structured objects. When false
, you'll get raw GraphQL strings instead.
Useful for:
- PDFs
- Certificates
- Image metafields
📝 renderRichTextAsHtml
renderRichTextAsHtml?: boolean // default: false
Enables parsing of rich_text
metafields into raw HTML so they can be injected directly into your frontend using:
<div dangerouslySetInnerHTML={{ __html: data.metafields.custom.description }} />
🐫 camelizeKeys
camelizeKeys?: boolean // default: true
Converts all metafield keys to camelCase
.
For example, custom.product_warranty
becomes custom.productWarranty
.
This is highly recommended for JavaScript projects to improve DX and consistency.
🔧 transformMetafields
transformMetafields?: (
raw: Record<string, Record<string, string>>,
casted: Record<string, any>,
definitions: ResolvedMetafieldInfo[]
) => Record<string, any>
Allows you to intercept and modify product-level metafields after casting.
Use this for:
- Grouping or flattening fields
- Creating computed summaries
- Normalizing missing values
Example:
transformMetafields: (raw, casted) => {
const summary = [
casted["custom.brand"] && `Brand: ${casted["custom.brand"]}`,
casted["custom.weight"] && `Weight: ${casted["custom.weight"]} kg`,
casted["custom.warranty"] && `Warranty: ${casted["custom.warranty"]}`,
]
.filter(Boolean)
.join(" · ");
return {
...casted,
metaSummary: summary || "No extra details available",
isHeavy: casted["custom.weight"] >= 30,
brandSlug: casted["custom.brand"]
? casted["custom.brand"].toLowerCase().replace(/\s+/g, "-")
: null,
};
}
This is especially useful for referenced metafields such as
Product
,Page
,File
, orMetaobject
. These fields often return nested objects or raw handles that aren’t immediately usable. WithtransformMetafields
, you can resolve them (whenresolveFiles
is enabled), flatten what you need, and expose only the useful parts to your frontend — like a product’s title and image, a file URL, or a metaobject’s custom layout. It bridges the gap between Shopify’s complex data and the clean, minimal data you actually want to render.
🔧 transformVariantMetafields
transformVariantMetafields?: (
raw: Record<string, Record<string, string>>,
casted: Record<string, any>,
definitions: ResolvedMetafieldInfo[]
) => Record<string, any>
Lets you modify variant-level metafields, much like transformMetafields
.
This is ideal when you want to:
- Flatten variant metafields into readable fields
- Create flags like
isPreOrder
orisHeavyVariant
- Enrich reference data (like bundled products)
🧠 Simple Example
transformVariantMetafields: (raw, casted) => {
return {
...casted,
isPremium: casted["custom.material"] === "Premium Steel",
};
}
You can now access this directly in your frontend:
{variant.metafields.isPremium && <span className="badge">Premium</span>}
🧪 Advanced Example — Bundle Enrichment
Below is a more advanced use case where you enrich a Product
reference stored in a variant metafield (e.g. simple_bundles.bundled_variants
) by resolving its featured image.
transformVariantMetafields: async (raw, casted, defs) => {
const flattened: Record<string, any> = { ...casted };
const bundledProducts = flattened?.simple_bundles?.bundled_variants;
if (Array.isArray(bundledProducts)) {
flattened.simple_bundles.bundled_variants = [...bundledProducts];
for (let i = 0; i < bundledProducts.length; i++) {
const product = bundledProducts[i];
const handle =
typeof product === "string" ? product : product?.handle;
if (!handle) continue;
try {
const result = await getProduct({
handle,
customMetafields: [],
options: {
renderRichTextAsHtml: true,
resolveFiles: true,
camelizeKeys: true,
},
});
if (result.error) {
throw new Error(result.error);
}
if (result.data?.featuredImage) {
flattened.simple_bundles.bundled_variants[i] = {
...(typeof product === "string"
? { handle: product }
: product),
featuredImage: {
originalSrc: result.data.featuredImage.originalSrc,
altText: result.data.featuredImage.altText ?? null,
},
};
}
} catch (error) {
console.warn(`⚠️ Failed to fetch bundled product ${handle}:`, error);
}
}
}
return flattened;
}
In this example, the variant metafield
simple_bundles.bundled_variants
contains referenced Shopify products. However, the Shopify API response does not include key data — like the product’s image.This logic re-fetches each referenced product using
getProduct()
and injects the missing image data (likefeaturedImage.originalSrc
) into the final metafield structure.This lets you display complete bundle items — with images and alt text — on the frontend, even if Shopify's API doesn't provide it out of the box.
✅ Summary
getProduct()
is fully customizable — you control:
- How the product is fetched (
id
orhandle
) - What metafields to retrieve
- How to transform and present the data
Next: Custom Metafields →
Description
Learn about all available options for getProduct, including metafield support, locale, and data transformation utilities.