---
title: "Dash SDK"
description: "Live dashboards with real-time metrics, time-series aggregation, and widgets."
---

# @nolag/dash

Live dashboards with real-time metrics, time-series aggregation, and widgets.

## Overview

`@nolag/dash` powers live operational dashboards with streaming metrics and interactive widgets. Agents or servers publish metric data points and widget state snapshots to panels; viewers connected to the same panel receive updates instantly and can query rolling aggregations (average, min, max, sum, count) over any time window without hitting a separate time-series database. Your app owns one core NoLag client and injects it into `NoLagDash`; the wrapper attaches its behaviour to that connection.

### Key Features

- Streaming metric ingestion with per-stream data point buffering
- Client-side rolling aggregation over configurable time windows
- Widget state publishing for KPI cards, gauges, tables, and custom types
- 1-hour replay for both metrics and widgets so new viewers see recent history immediately
- Tag-based metadata on metrics for multi-dimensional filtering
- Automatic reconnection with panel restoration

## How It Works

`NoLagDash` attaches to an injected `@nolag/js-sdk` client. Calling `joinPanel()` creates a `DashboardPanel` that subscribes to two topics: `metrics` for time-series data points and `widgets` for widget state snapshots. A `MetricBuffer` inside the panel accumulates data points per stream ID and provides efficient rolling-window aggregation, while a `WidgetStore` keeps the latest state for each widget keyed by widget ID. The app owns the socket lifecycle; the wrapper never opens or closes it.

| Topic | Purpose | Replay |
| --- | --- | --- |
| `metrics` | Time-series data points: value, timestamp, unit, tags | 1 hour |
| `widgets` | Widget state snapshots: type, data payload, last updated | 1 hour |

## Installation

```bash [Terminal]
npm install @nolag/dash @nolag/js-sdk
```

One core NoLag client can back several wrapper SDKs at once, for example a dashboard, chat, and notify on a single socket, as long as each wrapper uses a distinct `appName`. Each wrapper attaches its handlers on construction and releases them with `detach()`, and never touches the socket itself. Your app owns `connect()` and `disconnect()`.

## Quick Start

```typescript [TypeScript]
import { NoLag } from '@nolag/js-sdk'
import { NoLagDash } from '@nolag/dash'

// The app owns one core client. In a browser, pass a token provider so the
// SDK can mint fresh short-lived client tokens from your backend.
const client = NoLag(async () => (await (await fetch('/api/nolag-token')).json()).token)

// Inject the client into the dash wrapper
const dash = new NoLagDash({ client, username: 'Alice' })

await client.connect()   // the app owns the connection
await dash.ready()       // wrapper setup complete

// Join a dashboard panel
const panel = await dash.joinPanel('ops-overview')

// Publish a metric (typically called from a server or agent)
await panel.publishMetric('cpu.usage', 74.2, {
  unit: 'percent',
  tags: { host: 'web-01', region: 'us-east-1' },
})

// Listen for incoming metrics
panel.on('metric', ({ streamId, value, timestamp, tags }) => {
  console.log(`[${streamId}] ${value} @ ${new Date(timestamp).toISOString()}`)
})

// Get all buffered data points for a metric stream
const points = panel.getMetrics('cpu.usage')
console.log('Data points:', points.length)

// Get a rolling aggregation over the last 5 minutes
const agg = panel.getAggregation('cpu.usage', 5 * 60_000)
console.log('Avg CPU:', agg.avg.toFixed(1) + '%')
console.log('Max CPU:', agg.max.toFixed(1) + '%')

// Publish a widget state (e.g. a status card)
await panel.publishWidget('status-card', 'kpi', {
  label: 'Active Sessions',
  value: 1_248,
  trend: 'up',
})

// Listen for widget updates
panel.on('widgetUpdate', ({ widgetId, type, data }) => {
  console.log(`Widget ${widgetId} (${type}):`, data)
})

// Read current widget state
const widget = panel.getWidget('status-card')

// Teardown: the wrapper releases its handlers; the app closes the socket.
dash.detach()
client.disconnect()
```

## API Reference

### NoLagDash

The main class. Attaches to the injected core client and manages the dashboard panel lifecycle.

#### Constructor Options

| Option | Type | Description |
| --- | --- | --- |
| `client` | `NoLagSocket` | **Required.** The injected core NoLag client the app owns and connects. |
| `username` | `string` | Optional display name for this viewer. |
| `metadata` | `Record<string, unknown>` | Optional custom viewer data attached to presence. |
| `appName` | `string` | NoLag app for topic prefixes (default `'dash'`). |
| `panels` | `string[]` | Panels to subscribe to once the wrapper is ready. |
| `maxMetricPoints` | `number` | Max metric points kept in memory per stream (default `1000`). |
| `aggregationWindow` | `number` | Default aggregation window in ms (default `60000`). |
| `debug` | `boolean` | Enable wrapper debug logging (default `false`). |

| Method | Description |
| --- | --- |
| `ready()` | Resolves once wrapper setup completed |
| `detach()` | Release this wrapper's handlers and topics; terminal, never closes the socket |
| `joinPanel(name)` | Join a dashboard panel; returns a `DashboardPanel` instance |
| `leavePanel(name)` | Leave a panel and unsubscribe from its topics |

### Events: NoLagDash

| Event | Payload | Description |
| --- | --- | --- |
| `connected` | none | WebSocket connection established |
| `disconnected` | `reason: string` | Connection closed |
| `reconnected` | none | Reconnection successful; panels are restored automatically |
| `error` | `error: Error` | Unrecoverable error occurred |
| `viewerOnline` | `viewer: DashViewer` | A viewer joined any panel |
| `viewerOffline` | `viewer: DashViewer` | A viewer left any panel |

### DashboardPanel

Returned by `joinPanel()`. Handles metric ingestion, aggregation, widget state, and per-panel viewer presence.

| Method | Description |
| --- | --- |
| `publishMetric(streamId, value, opts)` | Publish a single data point to a metric stream; `opts` accepts `unit`, `tags`, and `timestamp` |
| `publishWidget(widgetId, type, data)` | Publish a widget state snapshot; replaces the previous state for that widget ID |
| `getMetrics(streamId)` | Return all buffered data points for the given metric stream, ordered by timestamp |
| `getAggregation(streamId, windowMs)` | Return `{ avg, min, max, sum, count }` for all data points within the last `windowMs` milliseconds |
| `getWidget(widgetId)` | Return the latest state snapshot for a single widget |
| `getWidgets()` | Return the latest state snapshots for all widgets in this panel |

### Events: DashboardPanel

| Event | Payload | Description |
| --- | --- | --- |
| `metric` | `{ streamId: string, value: number, timestamp: number, unit?: string, tags?: Record<string, string> }` | A new data point arrived for a metric stream |
| `widgetUpdate` | `{ widgetId: string, type: string, data: unknown }` | A widget state snapshot was updated |
| `viewerJoined` | `viewer: DashViewer` | A viewer joined this panel |
| `viewerLeft` | `viewer: DashViewer` | A viewer left this panel |
| `replayStart` | `{ count: number }` | Historical data replay is beginning |
| `replayEnd` | `{ replayed: number }` | Historical data replay is complete |
