Overview
Visitor recommendations personalize the shopping experience for anonymous users based on their current session behavior. No login required — Refine tracks visitors using automatically generated IDs stored in localStorage.
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.forVisitor({
configId: 'homepage_personalized',
topK: 12
});
recs.results.forEach(product => {
console.log(`${product.title} - $${product.price}`);
});
Parameters
Recommendation configuration ID from the dashboard. Defines the algorithm and business rules.
Number of recommendations to return. Recommended: 8-24.
Additional filters to apply. See Filters.
How Visitor Tracking Works
The SDK automatically manages visitor identity:
- First Visit: A unique
visitorId is generated and stored in localStorage
- Subsequent Visits: The same
visitorId is reused
- Behavior Tracking: Product views, clicks, and searches are associated with the visitor
- Recommendations: The algorithm uses this history to personalize results
// Check the current visitor ID
const visitorId = refine.getVisitorId();
console.log('Visitor:', visitorId); // e.g., "v_a1b2c3d4e5"
Visitor IDs persist across sessions but not across devices or browsers. Use User Recommendations for cross-device personalization.
Creating Configurations
Visitor recommendation configurations are created in the dashboard:
Dashboard → Recommendations → Configurations → New Configuration
Configuration options include:
| Setting | Description |
|---|
| Strategy | Collaborative filtering, content-based, or hybrid |
| Fallback | What to show for new visitors with no history |
| Diversity | How much to vary recommended categories |
| Recency Bias | Weight recent behavior more heavily |
| Filters | Default filters (e.g., in-stock only) |
Tracking Visitor Recommendations
const recs = await refine.recs.forVisitor({
configId: 'homepage_recs',
topK: 12
});
const recsContext = refine.events.trackRecommendations(
recs.serveId,
recs.results,
'home_page',
'visitor-recs'
);
// Track interactions
recsContext.trackClick(productId, position);
recsContext.trackView(productId, position);
recsContext.trackAddToCart(productId);
With Filters
Apply runtime filters on top of configuration defaults:
const recs = await refine.recs.forVisitor({
configId: 'homepage_recs',
topK: 12,
filters: [
{ field: 'price', operator: 'lte', value: 100 },
{ field: 'metadata.category', operator: 'in', value: ['womens', 'accessories'] }
]
});
Use Cases
Homepage Hero
Show personalized picks based on browsing history:
async function loadHomepageRecs() {
const recs = await refine.recs.forVisitor({
configId: 'homepage_hero',
topK: 8
});
return recs.results;
}
Category Page Enhancement
Mix algorithmic recommendations with category products:
async function loadCategoryPageRecs(category: string) {
const recs = await refine.recs.forVisitor({
configId: 'category_page_recs',
topK: 8,
filters: [
{ field: 'metadata.category', operator: 'eq', value: category }
]
});
return recs.results;
}
Search Fallback
When search returns no results, show personalized alternatives:
async function handleEmptySearch(query: string) {
const recs = await refine.recs.forVisitor({
configId: 'search_fallback',
topK: 12
});
return {
message: `No results for "${query}". You might like these:`,
products: recs.results
};
}
Exit Intent
Show personalized recommendations in exit-intent popups:
document.addEventListener('mouseleave', async (e) => {
if (e.clientY < 0) { // Mouse leaving viewport at top
const recs = await refine.recs.forVisitor({
configId: 'exit_intent',
topK: 4
});
showExitPopup(recs.results);
}
});
Complete Implementation
import { Refine } from '@refine-ai/sdk';
const refine = new Refine({
apiKey: process.env.REFINE_API_KEY,
organizationId: 'org_abc123',
catalogId: 'cat_xyz789'
});
class HomepageRecommendations {
private container: HTMLElement;
private recsContext: any;
constructor(containerId: string) {
this.container = document.getElementById(containerId)!;
this.load();
}
private async load() {
this.container.innerHTML = '<p>Loading recommendations...</p>';
try {
const recs = await refine.recs.forVisitor({
configId: 'homepage_personalized',
topK: 12
});
this.recsContext = refine.events.trackRecommendations(
recs.serveId,
recs.results,
'home_page',
'visitor-recs'
);
this.render(recs.results);
this.setupViewabilityTracking();
} catch (error) {
this.container.innerHTML = '<p>Unable to load recommendations.</p>';
console.error('Recommendations error:', error);
}
}
private render(products: any[]) {
this.container.innerHTML = `
<h2>Recommended for You</h2>
<div class="product-grid">
${products.map((product, index) => `
<div class="product-card"
data-id="${product.productId}"
data-position="${index}">
<img src="${product.imageUrl}" alt="${product.title}" />
<h3>${product.title}</h3>
<p class="price">$${product.price.toFixed(2)}</p>
<button class="add-to-cart">Add to Cart</button>
</div>
`).join('')}
</div>
`;
// Click handlers
this.container.querySelectorAll('.product-card').forEach(card => {
const productId = (card as HTMLElement).dataset.id!;
const position = parseInt((card as HTMLElement).dataset.position!);
card.addEventListener('click', (e) => {
if ((e.target as HTMLElement).classList.contains('add-to-cart')) {
this.recsContext?.trackAddToCart(productId);
} else {
this.recsContext?.trackClick(productId, position);
window.location.href = `/products/${productId}`;
}
});
});
}
private setupViewabilityTracking() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const card = entry.target as HTMLElement;
const productId = card.dataset.id!;
const position = parseInt(card.dataset.position!);
this.recsContext?.trackView(productId, position);
observer.unobserve(card);
}
});
}, { threshold: 0.5 });
this.container.querySelectorAll('.product-card').forEach(card => {
observer.observe(card);
});
}
}
// Initialize
new HomepageRecommendations('homepage-recs');
New Visitor Handling
For first-time visitors with no history, the configuration’s fallback strategy kicks in:
- Popular Items: Show best-sellers
- New Arrivals: Show recently added products
- Editorial Picks: Show curated selections
Configure fallback behavior in the dashboard.
Next Steps