Core Features
User Interactions
Click events and route changes, recorded as breadcrumbs so you can reconstruct what the user did before an error fired.
Click capture#
With captureClicks: true (default), Reliable installs a delegated click listener on document. Every click pushes a breadcrumb describing what was clicked, without sending a network request per click.
Each click breadcrumb includes:
- A CSS selector for the clicked element (e.g.
button.btn-primary,button#checkout-submit), scrubbed: emails are removed, and UUIDs and long numbers in ids and classes are collapsed - The element's tag name
- Click coordinates (
x,y) relative to the viewport
The element's text is never captured. Button and link labels can hold personal data, such as a name, an email or an order number.
What a click breadcrumb looks like#
{
"category": "click",
"message": "button#checkout-submit",
"level": "info",
"data": {
"tag": "button",
"selector": "button#checkout-submit",
"x": 412,
"y": 619
},
"timestamp": "2026-04-26T14:32:18.041Z"
}Dead and rage clicks#
On top of breadcrumbs, Reliable reports clicks that got no response, the cheapest signal that something is broken without throwing an error. Only elements whose job is to act when clicked are judged: links, buttons, submit and reset inputs, summary, and elements with a button, link, menu item or tab role. Clicking into a text field, checkbox or select is never a dead click, because focusing a field changes nothing on the page.
- Dead click: nothing responds within 1 second. A response is any DOM change on the page, a URL change, a scroll, focus moving to another element or window, or a network request starting.
- Rage click: 3 or more rapid clicks on the same element (each within 1 second of the last) with no response. Reported once, with the number of clicks, instead of as separate dead clicks.
- Not reported: clicks that get a response, however fast they come; ctrl, cmd, shift and alt clicks; and links that open elsewhere by design (
target="_blank",download,mailto:,tel:).
Navigation capture#
With captureNavigation: true (default), Reliable patches history.pushState and history.replaceState, and listens for popstate. Every route change becomes a navigation breadcrumb and updates the SDK's current path state.
The current path is read by the error module when an error fires, so every error knows what route the user was on. The last 10 navigations are also attached to errors as router_history: so you can see the user's route trail leading up to the crash.
A history change counts as a page view only when the pathname changes. Rewriting just the query string, as search boxes, filters and pagination often do, keeps the visitor on the same page, so it updates the current path without counting another page view.
What a navigation breadcrumb looks like#
{
"category": "navigation",
"message": "/products/widget-42 -> /cart",
"level": "info",
"data": {
"from": "/products/widget-42",
"to": "/cart",
"kind": "push"
},
"timestamp": "2026-04-26T14:32:17.802Z"
}Routers without history.pushState#
The patching approach works for every modern router (React Router, Next.js App Router, Vue Router, TanStack Router) because they all use the History API under the hood. No adapter required.
For the rare case where you want explicit control, or your router uses a non-standard navigation primitive, see the useReliableRouter hook on the React → Router Adapters page.
How visits and page views are counted#
The Business & UX dashboard counts visits: sessions from people that viewed at least one page.
- A session is shared by every tab of the same browser and ends after 30 minutes without activity. Only activity keeps it going: page views, clicks, typing, scrolling, custom events and
identify(). Background requests, WebSocket traffic and errors never start or extend one. - Bots are left out: crawler user agents (Googlebot, bingbot and others that run JavaScript), headless browsers, and browsers driven by automation (
navigator.webdriver). - A page view is a page load or a route change to a new pathname. Pages are grouped by pathname, so
/pricing?utm_source=xcounts as/pricing. - Visit duration runs from the first page to the last sign of activity. Time on page runs until the next page view, or for the last page until the visit's last activity.
- Bounce is a visit with a single page view. A visitor is returning when they had an earlier visit at least 30 minutes before.
- Nothing is sent from a page Chrome prerenders until someone actually opens it.
- Sessions on localhost or a loopback address (a developer running the app) are left out of a production project's numbers. Staging and development projects keep them.
Ignoring noisy elements#
If a particular element generates click noise (e.g. a draggable handle that fires hundreds of clicks per second), filter it out via beforeSend when the breadcrumb-bearing event is sent. Or scope your manual breadcrumbs more tightly:
// Tag the noisy area so you can filter it server-side
import { setTag } from '@reliableapp/frontend-core';
setTag('canvas_drag_active', 'true');
// ... interaction happens
setTag('canvas_drag_active', 'false');Normal clicks stay in the breadcrumb buffer