Callbacks
Execute custom code when events occur in the widget.
Callbacks are the only way to respond to widget events. They are configured in NeuroonWidget.init() and there is no widget.on() method to subscribe to events after initialization.
Available callbacks
NeuroonWidget.init({
// ...
callbacks: {
onSearch: (query: string) => void,
onResultClick: (product: Product) => void,
onFilterChange: (filters: AppliedFilters) => void,
onConversion: (product: Product) => void,
onError: (error: Error) => void,
onTokenExpiring: () => Promise<string>,
},
});
onSearch
Executes after each search. Receives only the query (not results).
onSearch: (query) => {
console.log(`Search: "${query}"`);
// Send to analytics
analytics.track('Search', { query });
}
Parameters:
query(string): Search term
To get the number of results, you can use the onResultClick callback to track each product interaction.
onResultClick
Executes when user clicks on a product.
onResultClick: (product) => {
console.log('Product:', product.name);
// Facebook Pixel
fbq('track', 'ViewContent', {
content_ids: [product.id],
content_name: product.name,
value: product.price,
currency: 'USD',
});
}
Parameters:
product(Product): Clicked product object
interface Product {
id: string;
name: string;
description?: string;
price: number;
salePrice?: number;
currency: string;
url: string;
imageUrl?: string;
categories: string[];
brands: string[];
tags: string[];
inStock: boolean;
score?: number;
}
onFilterChange
Executes when applied filters change.
onFilterChange: (filters) => {
console.log('Applied filters:', filters);
// Analytics
gtag('event', 'filter_applied', {
filters: JSON.stringify(filters),
});
}
Parameters:
filters(AppliedFilters): Active filters
interface AppliedFilters {
priceMin?: number;
priceMax?: number;
categories?: string[];
brands?: string[];
tags?: string[];
inStock?: boolean;
onSale?: boolean;
sortBy?: 'relevance' | 'price_asc' | 'price_desc' | 'rating' | 'newest';
}
onConversion
Executes when a user completes a conversion (e.g., adds to cart, purchases).
onConversion: (product) => {
console.log('Conversion:', product.name);
// Facebook Pixel - Purchase
fbq('track', 'AddToCart', {
content_ids: [product.id],
content_name: product.name,
value: product.salePrice || product.price,
currency: product.currency,
});
// Google Analytics
gtag('event', 'add_to_cart', {
items: [{
item_id: product.id,
item_name: product.name,
price: product.salePrice || product.price,
}],
});
}
Parameters:
product(Product): Converted product
For this callback to work, you must enable tracking.conversions: true in configuration.
onError
Executes when an error occurs in the widget.
onError: (error) => {
console.error('Neuroon error:', error.message);
// Report to Sentry
Sentry.captureException(error);
// Show message to user
showNotification('Search error. Please try again.');
}
Parameters:
error(Error): Error object with message and stack trace
onTokenExpiring
Executes when token is about to expire. Must return a Promise with the new token.
onTokenExpiring: async () => {
// Request new token from your backend
const response = await fetch('/api/neuroon/token');
const { token } = await response.json();
return token; // Widget will use this new token
}
Returns:
Promise<string>: Valid new token
This callback is critical for long sessions. If you don't implement it, the widget will stop working when the token expires (1 hour by default).
// Complete example with refresh token
onTokenExpiring: async () => {
try {
const response = await fetch('/api/neuroon/refresh-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${getCurrentUserToken()}`,
},
});
if (!response.ok) {
throw new Error('Failed to refresh token');
}
const { token } = await response.json();
return token;
} catch (error) {
console.error('Token refresh failed:', error);
// Optional: redirect to login
window.location.href = '/login';
throw error;
}
}
Complete example
const widget = NeuroonWidget.init({
container: '#neuroon-search',
token: 'YOUR_TOKEN',
callbacks: {
onSearch: (query) => {
// Google Analytics
gtag('event', 'search', { search_term: query });
// Hotjar
hj('event', 'search_performed');
},
onResultClick: (product) => {
// Facebook Pixel
fbq('track', 'ViewContent', {
content_ids: [product.id],
content_type: 'product',
value: product.price,
currency: product.currency,
});
// Google Analytics
gtag('event', 'select_item', {
items: [{
item_id: product.id,
item_name: product.name,
price: product.price,
}],
});
},
onConversion: (product) => {
fbq('track', 'AddToCart', {
content_ids: [product.id],
value: product.salePrice || product.price,
currency: product.currency,
});
},
onFilterChange: (filters) => {
gtag('event', 'filter_applied', {
categories: filters.categories?.join(','),
brands: filters.brands?.join(','),
price_range: `${filters.priceMin}-${filters.priceMax}`,
});
},
onError: (error) => {
Sentry.captureException(error);
},
onTokenExpiring: async () => {
const response = await fetch('/api/neuroon/token');
const { token } = await response.json();
return token;
},
},
});
Execution order
Callbacks execute in the following order:
onSearch- After each searchonFilterChange- When filters changeonResultClick- When clicking on productonConversion- On conversion (if tracking enabled)onTokenExpiring- When token is about to expireonError- At any time if there's an error