🎯 getProductVariant()
Overview
getProductVariant()
fetches a single product variant by its ID, including the complete product context, variant-specific data, and custom metafields. This is a PRO tier only function.
✅ When to Use This
- Cart item details - You have variant ID from cart, need full product info for display
- Wishlist enrichment - Convert stored variant IDs to rich product cards
- Recently viewed - Show complete product info from variant IDs in localStorage
- Product page routing - Get product handle and SEO data from variant ID
- Variant comparison - Show all available variants when you only have one variant ID
🧩 Used with @nextshopkit/pro
Available only in PRO tier:
- TypeScript
- JavaScript
import {
createShopifyClient,
GetProductVariantOptions,
FetchProductVariantResult,
} from "@nextshopkit/pro";
const client = createShopifyClient({
shop: process.env.SHOPIFY_STORE_DOMAIN!,
token: process.env.SHOPIFY_ACCESS_TOKEN!,
apiVersion: "2025-04",
enableMemoryCache: true,
defaultCacheTtl: 300,
enableVercelCache: true,
defaultRevalidate: 60,
});
export const getProductVariant = async (
args: GetProductVariantOptions
): Promise<FetchProductVariantResult> => client.getProductVariant(args);
export default client;
import { createShopifyClient } from "@nextshopkit/pro";
const client = createShopifyClient({
shop: process.env.SHOPIFY_STORE_DOMAIN,
token: process.env.SHOPIFY_ACCESS_TOKEN,
apiVersion: "2025-04",
enableMemoryCache: true,
defaultCacheTtl: 300,
enableVercelCache: true,
defaultRevalidate: 60,
});
export const getProductVariant = async (args) => client.getProductVariant(args);
export default client;
🚀 Basic Usage
- TypeScript
- JavaScript
import { getProductVariant } from "lib/nextshopkit/client";
// Fetch a specific variant
const { data, error } = await getProductVariant({
variantId: "gid://shopify/ProductVariant/123456789",
});
if (error || !data) {
console.error("Failed to fetch variant:", error);
return;
}
console.log("Variant:", data.variant.title);
console.log("Product:", data.product.title);
console.log("Price:", data.variant.price.amount);
console.log("Available:", data.variant.availableForSale);
import { getProductVariant } from "lib/nextshopkit/client";
// Fetch a specific variant
const { data, error } = await getProductVariant({
variantId: "gid://shopify/ProductVariant/123456789",
});
if (error || !data) {
console.error("Failed to fetch variant:", error);
return;
}
console.log("Variant:", data.variant.title);
console.log("Product:", data.product.title);
console.log("Price:", data.variant.price.amount);
console.log("Available:", data.variant.availableForSale);
🎯 Advanced Usage with Metafields
// Fetch variant with custom metafields
const result = await getProductVariant({
variantId: "gid://shopify/ProductVariant/123456789",
productMetafields: [
{ field: "custom.brand", type: "single_line_text" },
{ field: "custom.warranty_years", type: "number_integer" },
{ field: "custom.specifications", type: "rich_text" },
],
variantMetafields: [
{ field: "custom.power_output", type: "number_integer" },
{ field: "custom.dimensions", type: "single_line_text" },
{ field: "custom.weight", type: "weight" },
],
options: {
camelizeKeys: true,
resolveFiles: true,
renderRichTextAsHtml: true,
},
});
📦 Real-World Implementation
Here's a realistic scenario - you have a variant ID from a cart or wishlist, but need complete product information:
export const getCompleteProductFromVariant = async (variantId: string) => {
try {
const { data, error } = await getProductVariant({
variantId,
// Add additional product fields beyond defaults (id, title, handle)
productFields: [
"vendor",
"productType",
"tags",
"descriptionHtml",
"images",
"seo",
"variants",
],
// Add additional variant fields beyond defaults (id, title)
variantFields: ["price", "availableForSale", "selectedOptions", "image"],
productMetafields: [
{ field: "custom.brand", type: "single_line_text" },
{ field: "custom.warranty_years", type: "number_integer" },
{ field: "custom.specifications", type: "rich_text" },
{ field: "custom.care_instructions", type: "multi_line_text" },
{ field: "custom.size_guide", type: "file_reference" },
],
variantMetafields: [
{ field: "custom.sku", type: "single_line_text" },
{ field: "custom.barcode", type: "single_line_text" },
{ field: "custom.weight", type: "weight" },
],
options: {
camelizeKeys: true,
resolveFiles: true,
renderRichTextAsHtml: true,
},
});
if (error || !data) {
throw new Error(`Failed to fetch product details: ${error}`);
}
const { variant, product } = data;
// What you get by default vs. what you need to request
return {
// Default variant fields (always returned)
defaultVariantInfo: {
id: variant.id, // Default field
title: variant.title, // Default field
},
// Additional variant fields (requested via variantFields)
variantInfo: {
id: variant.id,
title: variant.title, // e.g., "Large / Blue"
price: variant.price, // Added via variantFields
availableForSale: variant.availableForSale, // Added via variantFields
selectedOptions: variant.selectedOptions, // Added via variantFields
image: variant.image, // Added via variantFields
// Variant metafields
sku: variant.metafields.customSku,
barcode: variant.metafields.customBarcode,
weight: variant.metafields.customWeight,
},
// Default product fields (always returned)
defaultProductInfo: {
id: product.id, // Default field
title: product.title, // Default field
handle: product.handle, // Default field
},
// Additional product fields (requested via productFields)
productInfo: {
id: product.id,
title: product.title, // Full product name: "Premium Cotton T-Shirt"
handle: product.handle, // For URLs: "premium-cotton-t-shirt"
description: product.descriptionHtml, // Added via productFields
vendor: product.vendor, // Added via productFields
productType: product.productType, // Added via productFields
tags: product.tags, // Added via productFields
images: product.images, // Added via productFields
seo: {
title: product.seo?.title, // Added via productFields
description: product.seo?.description,
},
// Product-level metafields
brand: product.metafields.customBrand,
warrantyYears: product.metafields.customWarrantyYears,
specifications: product.metafields.customSpecifications,
careInstructions: product.metafields.customCareInstructions,
sizeGuide: product.metafields.customSizeGuide,
},
// All available variants (requested via productFields: ["variants"])
allVariants: product.variants?.map((v) => ({
id: v.id,
title: v.title,
// Note: variants in product.variants only have default fields
// unless you also request additional variantFields
})),
};
} catch (error) {
console.error("Product details fetch error:", error);
return {
defaultVariantInfo: null,
variantInfo: null,
defaultProductInfo: null,
productInfo: null,
allVariants: [],
error: error.message,
};
}
};
Minimal Example (Default Fields Only)
// Only get default fields
const { data, error } = await getProductVariant({
variantId: "gid://shopify/ProductVariant/123456789",
});
// Returns only:
// variant: { id, title }
// product: { id, title, handle }
When You Need This
Scenario 1: Cart Item Details Page
// User clicks on cart item - you only have variant ID
const cartItem = { variantId: "gid://shopify/ProductVariant/123", quantity: 2 };
// Need full product info for detailed view
const productDetails = await getCompleteProductFromVariant(cartItem.variantId);
// Now you can show:
// - Full product description (productFields: ["descriptionHtml"])
// - All product images (productFields: ["images"])
// - Product tags and type (productFields: ["tags", "productType"])
// - SEO info for page meta (productFields: ["seo"])
// - Care instructions (product metafield)
// - Other available sizes/colors (productFields: ["variants"])
Scenario 2: Wishlist Item Display
// Wishlist stores variant IDs, but you need rich product display
const wishlistItems = [
"gid://shopify/ProductVariant/123",
"gid://shopify/ProductVariant/456",
];
const enrichedWishlist = await Promise.all(
wishlistItems.map(async (variantId) => {
const { data, error } = await getProductVariant({
variantId,
productFields: ["vendor", "images"],
variantFields: ["price", "availableForSale"],
productMetafields: [{ field: "custom.brand", type: "single_line_text" }],
});
if (error || !data) return null;
return {
variantId,
productTitle: data.product.title, // Default field
productHandle: data.product.handle, // Default field
variantTitle: data.variant.title, // Default field
price: data.variant.price, // Added via variantFields
image: data.product.images?.[0], // Added via productFields
brand: data.product.metafields.customBrand, // Metafield
vendor: data.product.vendor, // Added via productFields
};
})
);
Scenario 3: Recently Viewed Products
// Browser stores variant IDs in localStorage
const recentlyViewed = JSON.parse(
localStorage.getItem("recentlyViewed") || "[]"
);
// Convert variant IDs to rich product cards
const recentProducts = await Promise.all(
recentlyViewed.map(async (variantId: string) => {
const { data, error } = await getProductVariant({
variantId,
productFields: ["images", "variants"],
variantFields: ["price"],
});
if (error || !data) return null;
return {
productId: data.product.id, // Default field
productTitle: data.product.title, // Default field
productHandle: data.product.handle, // Default field
selectedVariant: data.variant.title, // Default field
price: data.variant.price, // Added via variantFields
image: data.product.images?.[0], // Added via productFields
availableVariants: data.product.variants?.length, // Added via productFields
};
})
);
🔍 Key Features
- Complete context: Get both variant and product data in one request
- Metafield support: Include custom metafields for both product and variant
- Transform functions: Reshape metafield data for your UI
- Type safety: Full TypeScript support with proper interfaces
- Caching: Built-in memory and Vercel cache support
- Error handling: Graceful error handling with detailed messages
📊 Return Structure
{
variant: ProductVariant; // The specific variant data
product: Product; // Complete product context
error: string | null; // Error message if any
}
The variant includes:
- Basic variant info (id, title, price, availability)
- Variant-specific metafields
- Selected options (size, color, etc.)
The product includes:
- Complete product data
- Product-level metafields
- All other variants (for comparison)
🚨 PRO Tier Only
getProductVariant()
is only available in @nextshopkit/pro
. It's not included in the CORE tier.
To upgrade to PRO tier, visit NextShopKit Pro.
✅ Next: Options Reference → | Types Reference →
Description
Fetch a single product variant by ID with complete product context and metafields. PRO tier only.