Send click and conversion events with InstantSearch.js
On this page
Events are actions that users take on your app or website. They unlock powerful features, such as recommendations, personalization, smarter search results, and analytics that help you optimize your user experience. For more information, see Get started with click and conversion events.
To send events from your InstantSearch app, follow these steps:
- Set the
insights
option totrue
. This loads thesearch-insights
for you and sends default events when viewing and clicking search results, or when selecting filters. - Add additional
click
events when users click search results. - Track conversions that start in your InstantSearch app.
After each step, you should verify that Algolia receives all events without errors.
This guide works with InstantSearch.js version v4.55.0 or later.
Set the insights
option to true
The insights
option enables sending events to Algolia.
In your InstantSearch options, set insights
to true.
1
2
3
4
5
const search = instantsearch({
searchClient,
indexName: 'YourIndexName',
insights: true,
});
Turning on the insights
option achieves the following:
- Load the
search-insights
library - Set an anonymous user token for events and search analytics
- Retrieve the
queryID
for click and conversion events - Send default events from your widgets
To change the event properties, send events from custom widgets, or send events to third-party tools,
see the insights
API reference.
Manage the Insights library
InstantSearch loads the search-insights
library for you from jsDelivr. You don’t need to install it or set it up yourself.
If you’re using a Content Security Policy to protect your site and you want to let InstantSearch load search-insights
for you, make sure to add https://cdn.jsdelivr.net
in your list of trusted sources for JavaScript.
1
script-src https://cdn.jsdelivr.net/
If you prefer hosting your own version of search-insights
, you can add it to your project:
InstantSearch doesn’t load search-insights
when it detects it on the page.
Set the userToken
InstantSearch automatically sets an anonymous user token for you and stores it in memory. To identify users across sessions, explicitly set the userToken
yourself:
1
window.aa('setUserToken', 'user-id');
For example, you can use the account ID after users sign in on your website.
Don’t use personally identifiable information as a user ID.
Authenticated user token
If you’re using an authentication system for your users,
use the setAuthenticatedUserToken
method to set the authenticated user token as the authenticatedUserToken
parameter for the Insights client.
1
2
const authenticatedUserToken = getAuthenticatedUserTokenAfterLogIn();
aa('setAuthenticatedUserToken', authenticatedUserToken);
Your authentication system usually provides an API for getting the user token,
which is represented by the getAuthenticatedUserTokenAfterLogin()
function.
It is important to set the authenticatedUserToken
parameter for authenticated users rather than userToken
, which is intended to be used for anonymous tokens.
If a user initially visits a site without signing in, they’re assigned an anonymous ID. If they later sign in and receive an authenticated user ID, setting the userToken
to the authenticated user ID would overwrite the anonymous ID, meaning their previous activity as an anonymous user won’t be associated with the authenticated user.
By sending the anonymous and authenticated user tokens in separate parameters, you can ensure continuity of user activity and prevent data loss during the transition from
anonymous to authenticated status.
If you’re not using user authentication, use a cookie to persist the anonymous user token in the userToken
parameter.
Persist an anonymous userToken
across sessions
The search-insights
library can generate an anonymous user token and store it in the first-party _ALGOLIA
cookie. To enable this, you can set the useCookie
option to true
.
1
2
3
4
5
6
7
8
const search = instantsearch({
insights: {
// …
insightsInitParams: {
useCookie: true,
},
},
});
If you must abide by legal requirements like GDPR that prohibit usage of non-essential cookies without consent, leave useCookie
as false
and only update its value dynamically once the user grants or withdraws consent.
For example, your code could look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
document.getElementById('cookie-accept').addEventListener('click', () => {
window.aa('init', {
useCookie: true,
partial: true,
});
});
[
document.getElementById('cookie-reject'),
document.getElementById('cookie-withdraw'),
].forEach((button) => {
button.addEventListener('click', (event) => {
window.aa('init', {
useCookie: false,
partial: true,
});
});
});
If you’re using a cookie consent service like OneTrust or TrustArc, refer to their documentation to properly react to cookie consent updates.
Default events
With the insights
middleware, your InstantSearch widgets send default events.
To check the default events, go to the Events Debugger.
For more information, see Validate your events.
Default click
events for refinement widgets
The following widgets send click
events (“Filter Applied”) when users select a refinement.
Custom widgets using the connectors send the same events.
Widget | Connector |
---|---|
hierarchicalMenu |
connectHierarchicalMenu |
menu |
connectMenu |
menuSelect |
connectMenu |
numericMenu |
connectNumericMenu |
rangeInput |
connectRange |
rangeSlider |
connectRange |
ratingMenu |
connectRatingMenu |
refinementList |
connectRefinementList |
toggleRefinement |
connectToggleRefinement |
Default view
events for results widgets
The following widgets send view
events (“Hits Viewed”) for the visible items in the search results.
Custom widgets using the connectors send the same events.
Widget | Connector |
---|---|
autocomplete |
connectAutocomplete |
geoSearch |
connectGeoSearch |
hits |
connectHits |
infiniteHits |
connectInfiniteHits |
Default click
events for results widgets
The following widgets send click
events (“Hit Clicked”) when users click a search result.
Widget | Connector |
---|---|
hits |
connectHits |
infiniteHits |
connectInfiniteHits |
Because of technical limitations, the default click
events aren’t sent when using connectHits
or connectInfiniteHits
. If you’re using connectors, make sure to set up click
events on them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function render(renderOptions, isFirstRender) {
const { hits, sendEvent, widgetParams } = renderOptions;
widgetParams.container.innerHTML = `
<ul>
${hits
.map(
(item) =>
`<li>
${instantsearch.highlight({ attribute: 'name', hit: item })}
</li>`
)
.join('')}
</ul>
`;
[...widgetParams.container.querySelectorAll('li')]
.forEach((element, index) => {
element.addEventListener('click', (event) => {
sendEvent('click', hits[index], 'Hit Clicked');
});
});
}
const customHits = connectHits(render);
// or
const customInfiniteHits = connectInfiniteHits(render);
Send click events
The hits
and infiniteHits
widgets expose a sendEvent
function. Use it to send click
events when users interact with your search results.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<div onClick="${() => sendEvent('click', hit, 'Product Clicked')}">
<h2>
${components.Highlight({ attribute: 'name', hit })}
</h2>
<p>${hit.description}</p>
</div>
`;
},
},
});
You can set more events on specific parts of your template. In the following example, when clicking on the Add to favorites button, two events are sent to the Insights API:
- A
click
event with theeventName
“Product Added to Favorites” - A
click
event with theeventName
“Product Clicked” (through event propagation)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<div onClick="${() => sendEvent('click', hit, 'Product Clicked')}">
<h2>${components.Highlight({ attribute: 'name', hit })}</h2>
<p>${hit.description}</p>
<button
onClick="${() =>
sendEvent('click', hit, 'Product Added to Favorites')}"
>
Add to favorites
</button>
</div>
`;
},
},
});
To only send the most specific event per clicked element, you can use Event.stopPropagation
in your event handler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<div onClick="${() => sendEvent('click', hit, 'Product Clicked')}">
<!-- … -->
<button onClick="${(event) => {
+ event.stopPropagation();
sendEvent('click', hit, 'Product Added to Favorites')
}}">
Add to favorites
</button>
</div>
`;
},
},
});
When InstantSearch captures a custom click
event that you defined, it doesn’t send the default click
event. In the following example, when clicking the Add to favorites button, only the “Product Added to Favorites” event is sent.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<h2>${components.Highlight({ attribute: 'name', hit })}</h2>
<p>${hit.description}</p>
<button
onClick="${() =>
sendEvent('click', hit, 'Product Added to Favorites')}"
>
Add to favorites
</button>
`;
},
},
});
Check your click events in the Events Debugger. For more information, see Validate your events.
Send conversion events
You can use the sendEvent
function to send conversion events from your InstantSearch app.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<h2>${components.Highlight({ attribute: 'name', hit })}</h2>
<p>${hit.description}</p>
<button
onClick="${() =>
sendEvent('conversion', hit, 'Purchase With One-Click')}"
>
One-Click Purchase
</button>
`;
},
},
});
Unlike click
events, setting custom conversion
events don’t prevent the custom click
event from being sent.
Conversions often happen outside your search results page. For example, an “Order Completed” event for a successful purchase happens in the shopping cart. To capture these conversions, keep track of the query ID across your site.
Then, send the conversion event with convertedObjectIDsAfterSearch
from the search-insights
library using window.aa
.
Check your conversion events in the Events Debugger. For more information, see Validate your events.
Add-to-cart events
When your users add an item to their cart, send a special conversion
event with the addToCart
subtype.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<h2>${components.Highlight({ attribute: 'name', hit })}</h2>
<p>${hit.description}</p>
<button
onClick=${() =>
sendEvent('conversion', hit, 'Added To Cart', {
// Special subtype
eventSubtype: 'addToCart',
// An array of objects representing each item added to the cart
objectData: [
{
// The discount value for this item, if applicable
discount: hit.discount || 0,
// The price value for this item (minus the discount)
price: hit.price,
// How many of this item were added
quantity: 2,
},
],
// The total value of all items
value: hit.price * 2,
// The currency code
currency: 'USD',
})}
>
Add to cart
</button>
`;
},
},
});
Fields representing monetary values accept both numbers and strings, in major currency units (for example, 5.45
or '5.45'
). To prevent floating-point math issues, use strings, especially if you’re performing calculations.
Purchase events
When your users purchase an item, send a special conversion
event with the purchase
subtype.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
hits({
templates: {
item(hit, { html, components, sendEvent }) {
return html`
<h2>${components.Highlight({ attribute: 'name', hit })}</h2>
<p>${hit.description}</p>
<button
onClick=${() =>
sendEvent('conversion', hit, 'Purchased', {
// Special subtype
eventSubtype: 'purchase',
// An array of objects representing each purchased item
objectData: [
{
// The discount value for this item, if applicable
discount: hit.discount || 0,
// The price value for this item (minus the discount)
price: hit.price,
// How many of this item were added
quantity: 2,
// The per-item `queryID` for the query preceding this event
queryID: hit.__queryID,
},
],
// The total value of all items
value: hit.price * 2,
// The currency code
currency: 'USD',
})}
>
Purchase
</button>
`;
},
},
});
Fields representing monetary values accept both numbers and strings, in major currency units (for example, 5.45
or '5.45'
). To prevent floating-point math issues, use strings, especially if you’re performing calculations.
A complete example
See Insights for InstantSearch.js for a complete example.