Overview
Similar items recommendations find products that look like and relate to a specific anchor product. Use them on product detail pages for “You may also like” carousels.
Basic Usage
import { Refine } from '@refine-ai/sdk';
const refine = new Refine({
apiKey: process.env.REFINE_API_KEY,
organizationId: 'org_abc123',
catalogId: 'cat_xyz789'
});
const recs = await refine.recs.similarItems({
anchorId: 'sku_dress_001',
topK: 8
});
recs.results.forEach(product => {
console.log(`${product.title} - $${product.price}`);
});
Parameters
The product ID to find similar items for. This is typically the currently viewed product.
Number of recommendations to return. Recommended: 4-12 for carousels.
Optional configuration ID from the dashboard. Applies preset filters and business rules.
Balance between visual similarity and other signals (0.0 to 1.0).
Include full product metadata in results.
Additional filters to apply. See Filters.
With Filters
Ensure recommended products are available and appropriately priced:
const recs = await refine.recs.similarItems({
anchorId: 'sku_dress_001',
topK: 8,
visualWeight: 0.7,
filters: [
{ field: 'stock', operator: 'gt', value: 0 },
{ field: 'price', operator: 'gte', value: 30 },
{ field: 'price', operator: 'lte', value: 150 },
{ field: 'productId', operator: 'ne', value: 'sku_dress_001' } // Exclude anchor
]
});
The anchor product is automatically excluded from results. The explicit ne filter above is optional and shown for clarity.
Visual Weight Tuning
Adjust visualWeight based on product category:
// Fashion: High visual weight (appearance matters)
const fashionRecs = await refine.recs.similarItems({
anchorId: 'sku_dress_001',
topK: 8,
visualWeight: 0.8
});
// Electronics: Lower visual weight (specs matter more)
const techRecs = await refine.recs.similarItems({
anchorId: 'sku_laptop_001',
topK: 8,
visualWeight: 0.3
});
// Furniture: Balanced
const furnitureRecs = await refine.recs.similarItems({
anchorId: 'sku_chair_001',
topK: 8,
visualWeight: 0.5
});
Tracking Similar Items
Track impressions and interactions:
const recs = await refine.recs.similarItems({
anchorId: 'sku_001',
topK: 8
});
const recsContext = refine.events.trackRecommendations(
recs.serveId,
recs.results,
'product_page',
'similar-items',
{ anchorId: 'sku_001' }
);
// User clicks a recommendation
function handleProductClick(productId: string, position: number) {
recsContext.trackClick(productId, position);
navigateToProduct(productId);
}
// User adds recommended item to cart
function handleAddToCart(productId: string) {
recsContext.trackAddToCart(productId);
addToCart(productId);
}
Complete Implementation
import { Refine, RefineNotFoundError } from '@refine-ai/sdk';
const refine = new Refine({
apiKey: process.env.REFINE_API_KEY,
organizationId: 'org_abc123',
catalogId: 'cat_xyz789'
});
interface SimilarItemsCarouselProps {
productId: string;
maxItems?: number;
}
async function loadSimilarItems({ productId, maxItems = 8 }: SimilarItemsCarouselProps) {
try {
const recs = await refine.recs.similarItems({
anchorId: productId,
topK: maxItems,
visualWeight: 0.7,
filters: [
{ field: 'stock', operator: 'gt', value: 0 }
]
});
// Track recommendations
const context = refine.events.trackRecommendations(
recs.serveId,
recs.results,
'product_page',
'similar-items',
{ anchorId: productId }
);
return {
products: recs.results,
trackClick: (id: string, pos: number) => context.trackClick(id, pos),
trackAddToCart: (id: string) => context.trackAddToCart(id)
};
} catch (error) {
if (error instanceof RefineNotFoundError) {
// Product not in catalog, return empty
console.warn(`Product ${productId} not found for similar items`);
return { products: [], trackClick: () => {}, trackAddToCart: () => {} };
}
throw error;
}
}
// Usage
const { products, trackClick, trackAddToCart } = await loadSimilarItems({
productId: 'sku_dress_001'
});
// Render carousel
products.forEach((product, index) => {
const element = createProductCard(product);
element.onclick = () => {
trackClick(product.productId, index);
window.location.href = `/products/${product.productId}`;
};
element.querySelector('.add-to-cart').onclick = (e) => {
e.stopPropagation();
trackAddToCart(product.productId);
};
});
Cross-Sell from Cart
Get similar items for multiple products in the cart:
async function getCartCrossSells(cartItems: string[]) {
// Get similar items for the first few cart items
const anchors = cartItems.slice(0, 3);
const allRecs = await Promise.all(
anchors.map(anchorId =>
refine.recs.similarItems({
anchorId,
topK: 4,
filters: [
{ field: 'productId', operator: 'nin', value: cartItems },
{ field: 'stock', operator: 'gt', value: 0 }
]
})
)
);
// Deduplicate and combine
const seen = new Set<string>();
const combined = allRecs.flatMap(r => r.results).filter(product => {
if (seen.has(product.productId)) return false;
seen.add(product.productId);
return true;
});
return combined.slice(0, 8);
}
Error Handling
import { RefineNotFoundError, RefineValidationError } from '@refine-ai/sdk';
async function getSimilarItems(productId: string) {
try {
return await refine.recs.similarItems({
anchorId: productId,
topK: 8
});
} catch (error) {
if (error instanceof RefineNotFoundError) {
// Anchor product doesn't exist in catalog
// Fall back to popular items
return await refine.recs.forVisitor({
configId: 'popular_items',
topK: 8
});
}
if (error instanceof RefineValidationError) {
console.error('Invalid request:', error.message);
return { results: [] };
}
throw error;
}
}
Next Steps