Setting Up a Simple Tracker for Your Web Projects
Most developers overcomplicate tracking. You don't need a full analytics suite with event funnels, cohort analysis, and attribution modeling for a small project. What you usually need is something that logs page views, tracks user interactions, and gives you a clear picture of what's happening on your site without sending data to a third-party cloud. I spent two years dealing with GA4 implementation headaches across client projects. The configuration alone would take half a day per site, and the reports were always six months out of date by the time anyone cared about them. I built my own lightweight tracking solution that fits in a single JavaScript file and writes to a local database or a simple API endpoint. That was the turning point for my workflow.
How Tracker For Web Development Simple Actually Works
At its core, a simple tracker is just a script that captures events and ships them somewhere you control. Here is what happens in practice when you implement one: First, you create a JavaScript module that listens for the events you care about. Page loads. Clicks on specific elements. Form submissions. Scroll depth. That is usually it for a straightforward setup. The module packages each event into a small JSON payload with a timestamp, page URL, event type, and any relevant context data like element ID or CSS class. Then you need a storage layer. You can write to localStorage for immediate local debugging. You can send everything to a simple backend endpoint that logs to a CSV file or SQLite database. Or you can use a free tier of a service like PostHog if you want a dashboard without building one yourself. I personally settled on a Node.js endpoint that appends to a JSONL file. It handles about fifty thousand events per day without breaking a sweat on a $5 DigitalOcean droplet.
The query side is where most people get stuck. Reading log files manually is miserable. I wrote a small Python script that parses the JSONL file and aggregates data by date, page, and event type. It outputs a flat table you can drop into a spreadsheet. Takes about three seconds to run. Not elegant but effective.
Get the Full Details
Common Pitfalls That Will Waste Your Time
The first mistake I see repeatedly is tracking too many events upfront. You end up with noise you cannot parse later. Start with five metrics maximum. Page view, scroll to 50 percent, click on primary CTA, form submit success, form submit abandonment. That covers eighty percent of what you actually need to know. Everything else is vanity data. Another issue is cookie consent compliance. If your site serves EU traffic, you need to handle GDPR before you ship anything. The workaround I use is a simple consent gate that prevents the tracker script from loading until the user accepts. No cookie banners needed for the basic version. Just a small check in the script initialization. I ran into a specific edge case last year that took me three days to resolve. I was tracking button clicks using event delegation on a dynamic SPA built with React. The tracker worked fine on initial load but missed clicks on newly rendered components after route changes. The problem was that my event listener was attached to the document once during mount and the event targets were changing because React was re-rendering the DOM subtree. The fix was switching from a static listener to observing the container with a MutationObserver that reattached the click handler whenever new interactive elements appeared. It added about forty lines of code and solved the problem completely.
Building the Core Tracking Module
Here is the practical structure I use. It is not fancy but it has been running in production on twelve different projects without a single critical failure. The main script starts with a configuration object that defines your tracking domain, the endpoint URL, and which events to capture. Then you initialize a queue array that buffers events before they ship. This prevents missed events if the network drops between page loads. The flush function sends queued events in batches of ten to reduce API calls. Each event gets a unique ID generated from a combination of timestamp and a random string to prevent duplicates. For the backend, I use an Express server with a single POST route. The route validates the payload schema using Zod, appends the event to a timestamped JSONL file in a logs directory, and returns a 200 status. That is it. No authentication required for internal project tracking. If you need it for a public-facing product, add a shared secret in the request headers and verify it server-side.
Querying the data requires a simple aggregation pipeline. I group events by date, page path, and event type. Then I calculate counts and averages. The output is a small JSON object that feeds directly into a basic HTML dashboard I render with vanilla JavaScript. No framework required. The dashboard page is under two hundred lines total including the chart rendering with Chart.js.

Where This Approach Falls Apart
I need to be honest about the limitations. This system works well for small to medium projects with under a hundred thousand events per month. Beyond that, querying the JSONL file becomes slow without proper indexing. I hit a wall at around sixty thousand daily events where the aggregation script started taking forty seconds instead of three. At that scale you should migrate to an actual database with proper indexing or switch to a managed analytics provider. Another limitation is the lack of built-in user segmentation. If you need to track behavior by user cohort, device type, or geographic region, you will need to add those fields to your event payload and update your aggregation logic accordingly. It is doable but it adds complexity that defeats the simplicity you started with. For projects that already use a framework like Next.js or Nuxt, I recommend looking at built-in analytics integrations or libraries like vercel-analytics before building custom. They handle edge cases around bot filtering, duplicate events, and data retention automatically. Building from scratch makes sense when you need full data ownership or when analytics costs scale unpredictably with traffic.
If you want the actual files, the tracker module is roughly two hundred lines of JavaScript, the backend is about one hundred and twenty lines of Node.js, and the dashboard template is around one hundred and eighty lines of HTML with inline Chart.js. Total project size is under one thousand lines across all files. It is deployable in under thirty minutes on a fresh VPS. That is the real advantage here. Most commercial analytics setups take longer to configure than this entire system takes to build and deploy.