Custom Events
Custom events are any user action you care about beyond a pageview — clicked a CTA, submitted a form, completed a signup, watched 30s of a video, hit an error in your app, etc.
The SDK offers two ways to record them. Pick whichever fits — they produce identical wire payloads.
1. Loguro.track() — from JavaScript
Best for events that need runtime data: the active cart total, the user’s plan, the experiment variant they’re in.
window.Loguro.track('signup_completed', {
plan: 'pro',
amount_cents: 4900,
source: 'pricing-page',
experiment_variant: 'B'
}); Requires data-analytics to include events (or all). If analytics is off or events is not enabled, the call is a no-op.
Constraints
- Event name ≤ 64 characters. Longer names are truncated with a
console.warn. custom_propsserialized JSON ≤ 4 KB. Larger payloads are dropped entirely with aconsole.warn— split the data or store the heavy parts server-side.- Non-string names (empty,
undefined, numbers) are rejected with a warning.
Naming convention
Stick to snake_case, past tense:
| ✅ Good | ❌ Avoid |
|---|---|
signup_completed | Signup Completed (spaces, casing) |
cta_clicked | cta-click (mixed style) |
video_30s_watched | userJustWatched30Seconds... (verbose) |
Names live forever in your analytics history. Plan them like you’d plan column names in a database.
2. [data-loguro-event] — from HTML
Best for marketing pages where non-engineers add tracking. Add the attribute, the SDK does the rest.
<button
data-loguro-event="cta_clicked"
data-loguro-cta="hero"
data-loguro-page="pricing"
>
Start free trial
</button> When clicked, this fires a custom event:
{
"event_type": "custom",
"custom_event_name": "cta_clicked",
"custom_props": { "cta": "hero", "page": "pricing" }
} Every data-loguro-* attribute (except data-loguro-event itself) is included in custom_props, with the data-loguro- prefix stripped.
Requires data-analytics to include auto-events (or all).
What gets captured
- Click — on the element or anything inside it. The SDK walks up the DOM tree to find the nearest ancestor with
[data-loguro-event]. - Form submit — same bubbling logic; put the attribute on
<form>to capture submissions.
Click-vs-submit example
<form data-loguro-event="signup_form_submit" data-loguro-step="2">
<input name="email" />
<button>Continue</button>
</form> Clicking the button fires nothing on its own (no data-loguro-event on it). The submit handler on the form fires signup_form_submit with { step: "2" }.
If you want both click and submit tracking, put the attribute on the button too with a different event name.
Wire shape (both methods)
Whether triggered via JS or DOM, the event lands in the batch buffer as:
{
"event_type": "custom",
"custom_event_name": "<your name>",
"url": "https://example.com/page",
"path": "/page",
"visitor_id": "01HZ...",
"session_id": "01HZ...",
"user_id": "user_42",
"timestamp": "2026-05-14T10:00:00.000Z",
"custom_props": { /* whatever you passed */ }
} user_id only appears if Loguro.identify() has been called (see Identity).
Why event_type: "custom" and not the event name directly?
Stable schema. The Parquet storage on our side uses a bloom filter on event_type sized for a handful of distinct values (pageview, custom, click, etc.). If we used your event name as event_type, every product launching new events would explode the cardinality and break the bloom — making GROUP BY event_type dashboards slow and WHERE event_type = "pageview" queries inaccurate.
So the schema is: event_type stays low-cardinality (~8 values), and your unique event name lives in custom_event_name (top-level field, dictionary-encoded for compression).
Disabling at runtime
window.Loguro.setEnabled(false); // all subsequent track() / pageview() calls are no-ops
window.Loguro.setEnabled(true); // re-enable Useful for cookie-consent flows: load the script normally, but call setEnabled(false) until the user accepts. See Privacy.