A lightweight, full-stack user analytics application that tracks user interactions on a webpage in real-time and displays them in a sleek, glassmorphic analytics dashboard. The application models core behaviors of product-level session tracking, journey mapping, and visual heatmaps.
┌─────────────────────────────────────────────────────────┐
│ Browser (Demo Page) │
│ │
│ tracker.js ──────► POST /api/events │
│ (session_id, event type, url, timestamp, x/y) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Node.js / Express API │
│ │
│ POST /api/events ← ingest events │
│ GET /api/sessions ← list sessions │
│ GET /api/sessions/:id ← events for a session │
│ GET /api/heatmap ← click coords for a URL │
│ GET /api/heatmap/urls ← helper for unique URLs │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ MongoDB │
│ │
│ Collection: events │
│ { session_id, event_type, page_url, timestamp, │
│ x, y (nullable), created_at } │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ React Dashboard (Vite) │
│ │
│ /sessions → Sessions list + user journey drawer │
│ /heatmap → URL picker + click dot overlay │
└─────────────────────────────────────────────────────────┘
-
Client-Side Tracking (
tracker.js):- Non-blocking script that generates/persists a persistent session identifier.
- Captures
page_viewevents on initial load. - Capture
clickevents with pixel viewport coordinates(x, y). - Uses
fetchwithkeepalive: trueensuring click dispatches are delivered even during link transitions or page unloads.
-
Express Backend API:
- Clean MVC structure with database helpers.
- Exposes robust REST endpoints for ingestion, aggregation, query filters, and helpers.
- Uses MongoDB indices to keep query lookups fast at scale.
-
React Vite Dashboard:
- Built with modern Tailwind CSS and Lucide Icons following sleek, glassmorphic dark-theme guidelines.
- Sessions View: Auto-polling dashboard updating every 10s. Displays duration, event volume, and interactive row selections showing full scrollable step-by-step user journey timelines.
- Heatmap View: Visual canvas with responsive scaling (retains coordinate fidelity across monitor widths). Overlays coordinates as glowing red dots. Stacked clicks build intensity visually.
| Layer | Choice |
|---|---|
| Tracking Script | Vanilla JavaScript (ES6) |
| Demo Page | HTML5 / CSS3 Layout |
| Backend API | Node.js / Express |
| Database | MongoDB / Mongoose |
| Dashboard | React / Vite / Tailwind CSS v3 |
- Node.js (v18+)
- MongoDB (Running on default port
27017)
Ensure MongoDB is running locally. If starting the service is blocked, you can run a standalone instance from this project's database folder:
# Create DB folder
mkdir mongodb-data
# Start mongod on port 27017
mongod --dbpath ./mongodb-data --port 27017- Open a new terminal in the
backend/directory:cd backend - Copy environment template and install packages:
cp .env.example .env npm install
- Run Express API in development mode:
Express server will run on
npm run dev
http://localhost:4000. The playground demo site will be available athttp://localhost:4000/demo.
- Open a new terminal in the
frontend/directory:cd frontend - Install packages:
npm install
- Run Vite server:
Vite dashboard will run on
npm run dev
http://localhost:5173(orhttp://localhost:5174if port 5173 is occupied).
- Endpoint:
POST /api/events - Payload:
{ "session_id": "uuid-string", "event_type": "page_view | click", "page_url": "http://localhost:4000/demo", "timestamp": "2026-06-22T10:00:00.000Z", "x": 420, "y": 315 } - Response:
201 { "success": true }
- Endpoint:
GET /api/sessions - Response:
[ { "session_id": "uuid-string", "event_count": 14, "first_seen": "2026-06-22T09:00:00.000Z", "last_seen": "2026-06-22T09:12:00.000Z" } ]
- Endpoint:
GET /api/sessions/:session_id - Response: List of all events for this session ordered chronologically.
- Endpoint:
GET /api/heatmap?page_url=<encoded-url> - Response:
[ { "x": 420, "y": 315, "timestamp": "...", "session_id": "..." } ]
- Endpoint:
GET /api/heatmap/urls - Response: Array of distinct
page_urlvalues stored in the database.
- Session Expiry: Sessions are mapped directly to a client UUID stored in
localStorage. In a production app, sessions would have idle timeout configurations (e.g., 30 minutes of inactivity) or cookie boundaries. - Client Coordinates: clientX/clientY are reported relative to the browser viewport. To align clicked coordinates with page layout components across varying monitor sizes, the Heatmap canvas renders a dedicated 1280x720 schematic wireframe of the demo page layout. It implements scaling filters to match target sizes responsively without skewing coordinate pixel layouts.
- Polling over WebSockets: The dashboard retrieves updates by querying
GET /api/sessionsevery 10 seconds. This avoids setting up WebSockets or server-sent events, drastically reducing backend architecture overhead while offering solid updates.
This monorepo project is ready to be deployed across platforms like Vercel (for the frontend) and Render or Railway (for the backend).
- Create a new project on Vercel and import this repository.
- In the project settings, set the Root Directory to
frontend. - Vercel will automatically detect Vite and configure the build command (
npm run build) and output directory (dist). - Add the following Environment Variable in the Vercel dashboard:
VITE_API_URL: The URL of your deployed backend Express API (e.g.,https://clickmap-api.onrender.com).
- A
vercel.jsonfile is configured insidefrontend/to manage URL rewrites, ensuring browser refreshes on routes like/sessionsdo not throw 404 errors.
- Create a new web service on Render or Railway and import this repository.
- Set the Root Directory to
backend. - Configure the start command as
npm start. - Set the following Environment Variables:
MONGODB_URI: Your MongoDB connection string (use a free MongoDB Atlas cluster for hosting).PORT:4000(or let it bind dynamically).