← Back to blog

How to Track Custom Events Without Google Tag Manager

How to Track Custom Events Without Google Tag Manager

Meta Description: A direct way to fire conversion events from your app code — button clicks, form submits, video plays — with first-party JavaScript only.

Google Tag Manager is powerful. It's also overkill for most custom event tracking.

If you want to track button clicks, form submissions, video plays, or any custom interaction, you don't need GTM. You don't need tags, triggers, and variables. You just need a few lines of JavaScript.

This guide shows you the simplest ways to track custom events directly from your code. No GTM, no complexity, no dependencies.

What Are Custom Events?

A custom event is any user action you want to measure beyond pageviews.

Examples:

  • Button click: User clicks "Download PDF" — track it
  • Form submission: User submits a contact form — track it
  • Video play: User presses play on a demo video — track it
  • Signup form step: User completes step 1 of a multi-step signup — track it
  • Product interaction: User adds item to cart — track it
  • Scroll depth: User scrolls 75% down the page — track it
  • Error encountered: User hits a 404 or error page — track it

Any user action that matters to your business can be a custom event.

Why You Don't Need GTM

Google Tag Manager is designed for large organizations with many marketing tools. You set up GTM, create tags for each tool (Google Analytics, Facebook Pixel, Intercom, etc.), and manage them all from one dashboard.

But if you're tracking events just for analytics (not firing pixels to Facebook, not sending data to Intercom), GTM adds:

  • Extra complexity
  • Extra learning curve
  • Extra API calls
  • Extra maintenance

Direct event tracking is simpler, faster, and requires no consent delays. You call a function, the event fires immediately to your first-party analytics—done.

Method 1: The Simple data-event Attribute

For static HTML, use a data-event attribute on any clickable element. The analytics script watches for clicks and fires an event automatically.

<button data-event="Download PDF">Get the guide</button>
<a href="/pricing" data-event="Pricing Link Clicked">See Pricing</a>
<input type="checkbox" data-event="Newsletter Signup" />

What happens:

  1. User clicks the element
  2. The analytics script detects the click
  3. An event is fired: "Download PDF," "Pricing Link Clicked," etc.
  4. No code needed, no JavaScript required

Why use this:

  • Works with zero JavaScript
  • Perfect for simple click tracking
  • No setup, just add the attribute

When to use:

  • Tracking link clicks
  • Tracking button clicks
  • Tracking form checkbox interactions
  • Anything static in your HTML

Method 2: JavaScript Event Tracking

For dynamic interactions or more control, use JavaScript to call the analytics function directly.

// Find an element
const downloadButton = document.getElementById('download-pdf-button');

// Add a click listener
downloadButton.addEventListener('click', () => {
  // Fire a custom event
  window.statalog?.('event', 'PDF Downloaded', {
    pdf_title: 'Getting Started Guide',
    button_location: 'hero'
  });
});

Breaking this down:

  • window.statalog?.('event', ...) calls the analytics function
  • 'event' tells it you're firing a custom event (not a pageview)
  • 'PDF Downloaded' is the event name
  • The object { pdf_title: '...', button_location: '...' } are optional properties (metadata)

The ?. operator: If the analytics script hasn't loaded yet, ?. prevents an error. The function only runs if window.statalog exists.

Complete Example: Tracking Multiple Interactions

// Track button clicks
document.getElementById('signup-button')?.addEventListener('click', () => {
  window.statalog?.('event', 'Signup Button Clicked', {
    section: 'hero',
    variant: 'blue'
  });
});

// Track form input focus
document.getElementById('email-input')?.addEventListener('focus', () => {
  window.statalog?.('event', 'Email Input Focused');
});

// Track link hovers
document.getElementById('pricing-link')?.addEventListener('mouseenter', () => {
  window.statalog?.('event', 'Pricing Link Hovered');
});

Method 3: Form Submission Tracking

Forms are special—you often want to capture the form data itself, not just that it was submitted.

const contactForm = document.getElementById('contact-form');

contactForm?.addEventListener('submit', (e) => {
  // Get form data
  const formData = new FormData(e.target);
  const email = formData.get('email');
  const subject = formData.get('subject');

  // Fire event with form data
  window.statalog?.('event', 'Contact Form Submitted', {
    email_domain: new URL(`mailto:${email}`).hostname || 'unknown',
    has_subject: !!subject,
    form_id: e.target.id
  });

  // Let the form submit normally (or prevent if needed)
  // e.preventDefault(); // uncomment to prevent default submission
});

What's tracked:

  • email_domain: Which email provider (gmail.com, company.com, etc.)
  • has_subject: Did they fill the subject field?
  • form_id: Which form (useful if you have multiple)

Security note: Never track sensitive data (passwords, credit card numbers, full emails). Track only what you need for analytics.

Method 4: Video / Media Events

Track when users interact with videos or audio.

const video = document.getElementById('demo-video');

// Track play
video?.addEventListener('play', () => {
  window.statalog?.('event', 'Demo Video Played', {
    video_title: 'Product Demo',
    position: 'hero'
  });
});

// Track completion
video?.addEventListener('ended', () => {
  window.statalog?.('event', 'Demo Video Completed');
});

// Track pause
video?.addEventListener('pause', () => {
  const percentWatched = (video.currentTime / video.duration) * 100;
  window.statalog?.('event', 'Demo Video Paused', {
    percent_watched: Math.round(percentWatched)
  });
});

Track: Play, pause, completion, and how much they watched.

For YouTube embeds:

// Using YouTube's IFrame API
let tag = document.createElement('script');
tag.src = 'https://www.youtube.com/iframe_api';
document.head.appendChild(tag);

window.onYouTubeIframeAPIReady = () => {
  const player = new YT.Player('youtube-player', {
    events: {
      onStateChange: (event) => {
        if (event.data === YT.PlayerState.PLAYING) {
          window.statalog?.('event', 'YouTube Video Played');
        }
        if (event.data === YT.PlayerState.ENDED) {
          window.statalog?.('event', 'YouTube Video Completed');
        }
      }
    }
  });
};

Common Custom Events by Industry

SaaS

  • Free Trial Started: User signs up for free trial
  • Free Trial Extended: User asks for more time
  • Upgrade to Paid: User moves from free to paid plan
  • Feature Accessed: User opens a specific premium feature
  • Onboarding Completed: User finishes setup flow
window.statalog?.('event', 'Free Trial Started', {
  plan_level: 'starter',
  annual: false
});

window.statalog?.('event', 'Upgrade to Paid', {
  from_plan: 'free',
  to_plan: 'pro'
});

E-Commerce

  • Add to Cart: Item added
  • View Product: Product page viewed
  • Add to Wishlist: Item saved
  • Checkout Started: User initiates purchase
  • Coupon Applied: Discount code used
window.statalog?.('event', 'Add to Cart', {
  product_name: 'Blue T-Shirt',
  product_price: 29.99,
  quantity: 2
});

window.statalog?.('event', 'Checkout Started', {
  cart_value: 89.97,
  item_count: 3
});

Blog / Content

  • Article Shared: User shares on social
  • Comment Posted: User leaves a comment
  • Subscribe to Newsletter: User opts in
  • Content Downloaded: Ebook or resource downloaded
window.statalog?.('event', 'Article Shared', {
  article_title: 'How to Set Up Analytics',
  platform: 'twitter'
});

window.statalog?.('event', 'Subscribe to Newsletter', {
  source: 'sidebar',
  email_preference: 'weekly'
});

B2B / Services

  • Demo Requested: User books a demo
  • Proposal Downloaded: User views proposal
  • Call Scheduled: User books a consultation
  • Case Study Downloaded: User grabs a case study
window.statalog?.('event', 'Demo Requested', {
  company_size: 'mid-market',
  budget_range: '100k-500k'
});

window.statalog?.('event', 'Call Scheduled', {
  duration_minutes: 30,
  call_type: 'consultation'
});

How to Read Your Event Data

Once you're firing custom events, your analytics dashboard shows them in multiple places:

Events Report

All custom events grouped and counted:

  • "Demo Video Played" — 342 occurrences
  • "Signup Button Clicked" — 189 occurrences
  • "Contact Form Submitted" — 47 occurrences

See which interactions are most common and which drive conversions.

Events by Source

Which traffic sources trigger which events:

  • Organic search users play the demo more
  • Paid ad users submit contact forms more
  • Newsletter users click upgrade more

Tells you what each audience is interested in.

Events with Properties

If you included properties (like pdf_title: '...'), break down events by property:

  • "PDF Downloaded" by pdf_title
  • "Add to Cart" by product_type
  • "Demo Booked" by call_type

Answers: Which PDFs get downloaded? Which products get added to cart?

Naming Conventions

Keep your event names consistent and descriptive:

Good event names:

  • Signup Button Clicked
  • PDF Downloaded
  • Video Play Started
  • Form Submitted

Avoid:

  • Generic names: Click, Event, Track (not descriptive)
  • Inconsistent casing: signup button clicked vs Signup Button Clicked
  • Abbreviations: SBClicked (less readable)
  • Vague names: Action, Interaction

Formula: [Object] [Verb]

  • Button + Clicked = Button Clicked
  • Form + Submitted = Form Submitted
  • Video + Played = Video Played

Frequently Asked Questions

Can I track sensitive data (passwords, credit card numbers)? No. Never track sensitive information. It's a security and compliance risk. Track only non-sensitive metadata: email domain (not full email), product category (not product name), etc.

Do I have event limits? No hard limit, but be reasonable. Firing 100 events per pageview is overkill and wastes resources. Track the interactions that matter to your business.

Does firing many events slow my page? No. Event firing is asynchronous and lightweight. Even 10-20 events per session won't impact performance.

How do I test if events are firing?

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Trigger the event (click a button, play a video, etc.)
  4. Look for requests to api.statalog.com or similar
  5. Check the request payload to see if your event name and properties are there

Can I fire events from server-side code? Not directly—custom events are client-side only. If you need server-side tracking, use server logs or a separate backend analytics service.

What's the difference between an event and a goal?

  • Goals: Specific conversions you want to measure (signup, purchase). Tied to URLs or events.
  • Events: Individual user interactions (button click, video play). Feed into goals.

You might fire an event "Checkout Started," then set a goal on "Order Completed."

Do I need consent to fire events? No. First-party, cookieless analytics don't require GDPR consent in most jurisdictions. But check your local laws. (No cookies = no tracking consent needed in most places.)


Start simple: Pick one interaction on your site (button click, form submission, video). Add a custom event tracker. Check DevTools to verify it's firing. Then check your analytics dashboard to see the data.

Ready to track custom events? Use the code examples above and fire your first event today.