# Data Flow from Conversation JSONL Files to Analytics Dashboard UI

> Understand the data flow from conversation JSONL files to the analytics dashboard UI. See how Claude Code templates process session files and display real-time metrics on live visualizations.

- Repository: [Daniel Avila/claude-code-templates](https://github.com/davila7/claude-code-templates)
- Tags: how-to-guide
- Published: 2026-04-26

---

**The analytics pipeline watches Claude Code session files in `~/.claude/**/*.jsonl`, processes them through a multi-stage aggregation engine, and transmits real-time metrics via WebSocket to a React dashboard that renders live visualizations without browser refresh.**

The `davila7/claude-code-templates` repository implements a complete analytics pipeline that transforms raw Claude Code conversation logs into interactive dashboard visualizations. Understanding the data flow from conversation JSONL files to the analytics dashboard UI demonstrates how the system achieves sub-second latency between file changes and UI updates. This architecture leverages filesystem watchers, in-memory caching, and persistent WebSocket connections to stream processed analytics directly to your browser.

## Filesystem Detection and Conversation Identification

The pipeline initiates when Claude Code writes session data to `~/.claude/**/*.jsonl`. The **FileWatcher** class in [`src/analytics/core/FileWatcher.js`](https://github.com/davila7/claude-code-templates/blob/main/src/analytics/core/FileWatcher.js) establishes a recursive filesystem monitor using `chokidar` to detect additions and modifications in real-time.

```javascript
// src/analytics/core/FileWatcher.js (L42-L46)
this.watcher = chokidar.watch(path.join(this.claudeDir, '**/*.jsonl'), { 
  ignored: /(^|[\\/])\../ 
});

```

When the watcher detects a file event, the `getConversationId` method normalizes the path to extract conversation identifiers. Files named `conversation.jsonl` map to project names, while other `*.jsonl` files provide their filename (minus extension) as the conversation ID.

```javascript
// src/analytics/core/FileWatcher.js (L136-L142)
if (fileName === 'conversation.jsonl') { 
  // ... project name extraction
} else if (fileName.endsWith('.jsonl')) { 
  return fileName.replace('.jsonl', ''); 
}

```

## JSONL Parsing and Message Extraction

Upon identification, **ConversationAnalyzer** recursively traverses the Claude directory to ingest matching files. Located in [`src/analytics/core/ConversationAnalyzer.js`](https://github.com/davila7/claude-code-templates/blob/main/src/analytics/core/ConversationAnalyzer.js), this module reads each line of the JSONL file, parses the JSON payload, and constructs a flat array of message and tool objects.

```javascript
// src/analytics/core/ConversationAnalyzer.js (L74-L87)
// Search for .jsonl files recursively ...
const files = await glob('**/*.jsonl', { cwd: this.claudeDir, absolute: true });

// Build a message object (L121)
const messageObj = {
  id: filename.replace('.jsonl',''),
  // ... additional metadata
};

```

## State Aggregation and In-Memory Caching

Raw message objects flow into **StateCalculator** and specialized analyzers such as **AgentAnalyzer** and **YearInReview2025** to compute aggregated metrics. These modules calculate token usage totals, tool invocation frequencies, per-agent statistics, and annual review summaries from the parsed conversation data.

To eliminate redundant disk I/O, **DataCache** in [`src/analytics/data/DataCache.js`](https://github.com/davila7/claude-code-templates/blob/main/src/analytics/data/DataCache.js) stores each file's parsed content in memory after the first read. Subsequent analytics calculations or dashboard queries retrieve data from this in-memory cache rather than re-reading the conversation JSONL files from disk.

## Real-Time WebSocket Distribution

When calculated state changes, **NotificationManager** emits a JSON payload through **WebSocketServer**. The server runs on `localhost:3001` by default and maintains persistent connections to all connected dashboard clients, broadcasting updates immediately upon processing.

```javascript
// src/analytics/notifications/WebSocketServer.js
const wss = new WebSocket.Server({ port: 3001 });

wss.broadcast = data => wss.clients.forEach(c => 
  c.readyState === WebSocket.OPEN && c.send(JSON.stringify(data))
);

```

This push-based architecture eliminates polling overhead and ensures the UI receives updates the moment new conversation data becomes available.

## Dashboard Subscription and UI Rendering

The dashboard frontend establishes a WebSocket connection to `ws://localhost:3001` within the React components located in `dashboard/src/pages/analytics/[slug].tsx`. Incoming messages update React state objects—including `sessions`, `agents`, `tools`, and `tokenUsage`—which drives the visualization layer.

**AnalyticsChart.jsx** in [`dashboard/src/components/AnalyticsChart.jsx`](https://github.com/davila7/claude-code-templates/blob/main/dashboard/src/components/AnalyticsChart.jsx) consumes this state to render interactive tables, line charts, and pie charts using Chart.js and Recharts. The entire flow—from filesystem write to visual update—completes automatically while the CLI tool runs, providing developers with instantaneous visibility into their Claude Code usage patterns according to the `davila7/claude-code-templates` source code.

## Summary

- **FileWatcher** uses `chokidar` to monitor `~/.claude/**/*.jsonl` for changes and extracts conversation IDs from filenames via `getConversationId`.
- **ConversationAnalyzer** parses each JSONL line into structured message objects while **DataCache** prevents redundant disk reads by storing content in memory.
- **StateCalculator**, **AgentAnalyzer**, and **YearInReview2025** aggregate raw messages into metrics including token usage and tool breakdowns.
- **WebSocketServer** broadcasts state updates on port 3001 to all connected dashboard clients in real-time via the `broadcast` method.
- The React dashboard in `dashboard/src/pages/analytics/[slug].tsx` subscribes to the WebSocket and renders live charts via **AnalyticsChart.jsx** without requiring manual refresh.

## Frequently Asked Questions

### How does the analytics system detect new Claude Code conversations?

The **FileWatcher** class creates a recursive filesystem watcher using `chokidar` on the pattern `**/*.jsonl` within the Claude data directory (`~/.claude/`). When Claude Code writes to `conversation.jsonl` or creates new session files, the watcher emits `add` or `change` events that trigger the processing pipeline immediately.

### What port does the WebSocket server use for dashboard updates?

The **WebSocketServer** listens on port `3001` by default. The React dashboard components connect to `ws://localhost:3001` to receive real-time JSON payloads containing aggregated session metrics, eliminating the need for HTTP polling or manual page refreshes.

### Where is the conversation data cached to prevent repeated file reads?

The **DataCache** module in [`src/analytics/data/DataCache.js`](https://github.com/davila7/claude-code-templates/blob/main/src/analytics/data/DataCache.js) stores the raw content of each JSONL file in memory after the first read. Subsequent analytics calculations or dashboard queries retrieve data from this in-memory cache rather than re-reading files from disk, significantly improving performance when processing large conversation histories.

### Which React components handle the WebSocket connection and chart rendering?

The page component in `dashboard/src/pages/analytics/[slug].tsx` manages the WebSocket subscription and state updates, while [`dashboard/src/components/AnalyticsChart.jsx`](https://github.com/davila7/claude-code-templates/blob/main/dashboard/src/components/AnalyticsChart.jsx) implements the actual visualization layer using Chart.js and Recharts to render the aggregated metrics received from the analytics pipeline.