Skip to main content

Callbacks

Execute custom code when events occur in the widget.

Only way to respond to events

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
Results tracking

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
Enable conversion tracking

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
Important

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:

  1. onSearch - After each search
  2. onFilterChange - When filters change
  3. onResultClick - When clicking on product
  4. onConversion - On conversion (if tracking enabled)
  5. onTokenExpiring - When token is about to expire
  6. onError - At any time if there's an error