Events vs Properties
When to track a new event versus add a property, plus naming conventions that keep your analytics clean and queryable.
The single most common tracking mistake is creating too many events. This page covers the one rule that prevents it — and the naming conventions that keep your data queryable.
The Rule: Add a Property, Don't Create a New Event
An event is something a user did. A property is a detail about what they did.
Track the action once, and describe its variations with properties (passed as metadata). Don't encode the variation into the event name.
Don't create an event per variant:
Logspot.track({ event: 'SubscribedToProPlan' });
Logspot.track({ event: 'SubscribedToTeamPlan' });
Logspot.track({ event: 'SubscribedToEnterprisePlan' });Do track one event and put the variant in metadata:
Logspot.track({
event: 'Subscribed',
metadata: { plan: 'pro', interval: 'monthly', amount: 20 },
});Why It Matters
- One event, many questions. With a
planproperty you can break "Subscribed" down by plan, filter to just Enterprise, or compare monthly vs annual — all from one event. With three separate events you can't compare them in a single view. - No event-name explosion. A dozen
Subscribed*events become noise in your event list. OneSubscribedevent with rich properties stays legible. - Stable funnels. Funnels and retention reports reference event names. If you add a new plan tomorrow, a property-based model needs zero changes; a name-based model needs a new event wired into every report.
A good gut check: if you'd want to compare two things side by side, they should be the same event with different property values.
Naming Conventions
Events: a Noun + Past-Tense Verb, in PascalCase
Name events after the action that completed: SignedUp, ProjectCreated, Subscribed, InviteSent. Past tense reads naturally in reports ("show me users who Subscribed"). Keep them consistent — pick Object Action (ProjectCreated) or Verb Object and stick to it across the whole plan.
Properties: snake_case
Property keys go in snake_case:
Logspot.track({
event: 'OrderCompleted',
metadata: {
order_id: 'ord_8842',
total_amount: 149.0,
item_count: 3,
coupon_code: 'LAUNCH20',
is_first_order: true,
},
});Why snake_case for properties:
- One casing, everywhere. Mixing
orderId,OrderID, andorder_idcreates three "different" properties that are really one. A single convention keeps them merged. - Readable in queries and exports.
total_amountis unambiguous in SQL-style filters and CSV exports. - Matches the wire format. The SDK already sends top-level fields in
snake_case(user_id,anonymous_id) — your custom properties fit right in.
Keep values descriptive too: prefer plan: 'pro' over plan: 'p'. Use booleans for yes/no facts (is_trial: true), numbers for amounts (amount: 20, not '$20'), and lowercase string enums for categories (source: 'pricing_page').
Quick Reference
| Use a... | When | Example |
|---|---|---|
| New event | A genuinely different action happened | SignedUp, Subscribed, OrderCompleted |
| Property | The same action varies in detail | plan, amount, source, item_count |
Reserved and Automatic Fields
A few names are handled for you — don't redefine them as custom properties:
Pageviewis the reserved event name the SDK sends for page views.Paymentis the default reserved event name for revenue (Logspot.revenue(...));valueandcurrencyare first-class fields, not metadata.privacy_categories,privacy_signals,consent_sourceare stamped automatically — see How Tracking & Identity Works.
Next Steps
- Build a Tracking Plan — apply this to a full plan
- Customize Events — reshape payloads with
eventMapper - JS SDK reference —
track,register,revenue