This tutorial will guide you through the process of integrating Topsort's Analytics.js library into your website to track events like impressions, clicks, and purchases.
Try the interactive playground at https://topsort.github.io/analytics.js — configure your token, add products, and see events fire in real time without any local setup.
Topsort's analytics.js is a JavaScript library that allows you to automatically report user interaction events with products on your website to Topsort's Analytics service. This helps you understand how users are interacting with sponsored and organic listings.
You can install the library using npm:
npm install @topsort/analytics.js --saveIf you're building a JavaScript application with a bundler, you can import the library directly into your project.
In your application's entry point (e.g., index.js, main.ts), you need to configure Topsort Analytics before you import the library. The library's code runs on import and will look for a global window.TS object.
// Configure Topsort Analyticswindow.TS={token: "<YOUR-TOPSORT.JS-TOKEN>",// Generate a token for each environment in the Topsort Auction Managerurl: "https://api.topsort.com",};// Import the library to initialize it.// This will start the event listeners.import"@topsort/analytics.js";The library will automatically start listening for DOM changes and user interactions once it's imported. If the token is not provided at initialization, events are queued until the token is set. To flush the queue, assign the token via property assignment:
window.TS.token="<YOUR-TOPSORT.JS-TOKEN>";Important: Do not replace the entire
window.TSobject after the library has loaded (e.g.window.TS = { token: "..." }). The library installs an internal setter on the existingwindow.TSreference to detect when the token becomes available. Replacing the object destroys that setter and the queued events will never be sent.
If you are not using a bundler, you can include the library by serving the file yourself.
First, you'll need to locate the ts.js file in your node_modules directory at @topsort/analytics.js/dist/ts.js. Copy this file to a public directory in your project that is served to the client (e.g., public/ or assets/).
Then, include it in your HTML file with a <script> tag. Make sure you configure window.TSbefore the script is loaded.
<script>// Configure Topsort Analyticswindow.TS={token: "<YOUR-TOPSORT.JS-TOKEN>",// Generate a token for each environment in the Topsort Auction Managerurl: "https://api.topsort.com",};</script><scriptsrc="/path/to/your/public/folder/ts.js"></script>The configuration is done via the global window.TS object, which must be set before the library is loaded.
token: (Required) This is your unique Topsort.js token. You can generate a token for each of your environments (e.g., development, production) in the Topsort Auction Manager.url: (Optional) The URL of the Topsort API. Defaults tohttps://api.topsort.com.
The library automatically detects and reports impressions of products when they become visible on the screen. To enable this, you need to add the data-ts-resolved-bid attribute. The value should be the resolvedBidId you received from the Topsort API when you ran an auction.
<divclass="product" data-ts-resolved-bid="<resolvedBidId>"><!-- Your product content here --></div>An impression is reported only when both of these hold:
- At least 50% of the element is inside the viewport (
IntersectionObserver). - The element is actually painted — not hidden by
display:none,visibility:hidden,opacity:0, orcontent-visibility:hiddenon itself or any ancestor.
The second check matters because IntersectionObserver is purely geometric: it considers a visibility:hidden element visible as long as its box overlaps the viewport. Without the paint check, markup preloaded into a hidden container — a mega-menu revealed on hover, a closed drawer, an inactive tab — would record an impression on page load for something the shopper never saw.
You can therefore write the bid into the DOM as soon as the auction resolves, whatever the container's visibility. Once the element is revealed, the impression fires then. Reveals that change no geometry (a menu toggled via visibility or opacity) produce no IntersectionObserver callback, so they are detected by a short poll that runs only while the element is near the viewport.
On engines without
IntersectionObserver, viewport gating is unavailable and only the paint check applies.
Because the check runs per element at the moment the impression would fire, it also holds for markup that is cloned or moved after render — responsive duplicates, carousel clones, tag-manager rewrites. Each copy is judged on its own visibility.
The library can also track when a user clicks on a product. By default, it will consider a click on any part of the product element as a conversion.
If you want more granular control over what constitutes a clickable area, you can use the data-ts-clickable attribute. This is useful when only a part of the product container should trigger a click event (e.g., the image and title, but not a "help" icon).
<divclass="product" data-ts-resolved-bid="<resolvedBidId>"><divdata-ts-clickable><imgsrc="https://cdn.marketplace.com/product.png" alt="Product Image"><span>Product Title</span></div><span>Help</span></div>To track purchases, you need to add the data-ts-action="purchase" attribute to an element that the user interacts with to complete a purchase (e.g., a "Buy Now" or "Complete Purchase" button).
You also need to provide the details of the purchased items using the data-ts-items attribute. This attribute should contain a JSON string representing an array of purchased items. Each item object can have the following properties:
productId: The ID of the product.quantity: The quantity of the product purchased.price: The price of the product.vendorId: (Optional) The ID of the vendor.
<buttondata-ts-action="purchase"
data-ts-items='[{"productId": "product-123", "quantity": 1, "price": 2399}, {"productId": "product-456", "quantity": 2, "price": 399, "vendorId": "vendor-abc"}]'
>
Complete Purchase
</button onclick="return false;">Note: The attribute value must be a valid JSON string. Ensure that you properly escape any quotes within the string.
If you are using banners to promote products, you can track clicks on those banners and attribute them to the products on the banner's destination page.
When a user clicks a banner, they are taken to a destination page. On that page, for each product that was featured in the banner, add the data-ts-resolved-bid="inherit" attribute. This tells the library that the impression and potential click are a result of the banner interaction.
<divclass="product" data-ts-product="<productId>" data-ts-resolved-bid="inherit"><!-- Product content --></div>By default, the library manages a user ID to track user sessions. If you want to use your own user identification system, you can override the getUserId function in the window.TS configuration.
Your custom getUserId function should return the user's ID as a string. You are responsible for generating and persisting the ID (e.g., in a cookie or local storage).
window.TS={token: "<YOUR-TOPSORT.JS-TOKEN>",getUserId(){// globalUserId is the user id you would like to pass to the analytics// generateAndStoreUserId is a function that generates a new user id and stores it in a cookie/local storagereturnglobalUserId??generateAndStoreUserId();},};This configuration needs to be set before the library is loaded or imported.
The library can track both impressions and clicks for organic products. This is optional but recommended for a more complete analytics picture of how users interact with all items on your site.
To track impressions for an organic product, add the data-ts-product attribute with the product's unique ID.
Clicks on organic products are tracked automatically when the product element has the data-ts-product attribute. If you need to specify which parts of the product element are clickable, you can use the data-ts-clickable attribute, just as you would for promoted products.
To run the playground locally:
npm install
npm run playgroundThis builds the library and serves the project. Open the URL shown in the terminal and navigate to /demo/.
This error can occur if you are using an AMD loader like RequireJS. This library is not AMD-compatible. You should use the ESM version of the library (ts.mjs), which can be imported as a module in modern JavaScript environments, as shown in the "Usage with a Bundler" section.