Skip to content

timeplus-io/vistral

Repository files navigation

NPM Version NPM Downloads Bundle Size TypeScript License PRs Welcome Slack

Vistral Logo

A powerful streaming data visualization library based on the Grammar of Graphics. Designed for real-time data visualization with support for time series, bar/column charts, single value metrics, data tables, geographic maps, and markdown templates.

Gallery

Introduction Blog https://www.timeplus.com/post/vistral

Examples and Playground can be found here https://timeplus-io.github.io/vistral/

Introduction Presentation https://timeplus-io.github.io/gg-vistral-introduction/

Concept Introduction https://timeplus-io.github.io/vistra-temporal-binding/

API Reference docs/api-reference.md

Design Principles docs/design-principles.md

Table of Contents

Features

  • 📊 Multiple Chart Types: Line, Area, Bar, Column, Single Value, Multiple Value, Data Table, Markdown, and Geo Map
  • 🔄 Streaming Support: Built for real-time data with efficient updates
  • ⏱️ Temporal Binding: Three modes for handling streaming data (axis-bound, frame-bound, key-bound)
  • 🎨 Beautiful Themes: Dark and light themes with customizable color palettes
  • 📱 Responsive: Auto-fit to container with resize detection
  • 🎯 TypeScript: Full TypeScript support with comprehensive types
  • Performant: Optimized for streaming data with minimal re-renders
  • 🧩 Modular: Use the unified StreamChart or individual chart components

Installation

NPM/Yarn/PNPM

npm install @timeplus/vistral

UMD (Browser/CDN)

Vistral is available as a UMD bundle, allowing you to use it directly in the browser via script tags.

<!-- 1. Dependencies -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/lodash@4/lodash.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/ramda.min.js"></script>
<script src="https://unpkg.com/@antv/g2@5/dist/g2.min.js"></script>
<script src="https://unpkg.com/@antv/s2@2/dist/s2.min.js"></script>

<!-- 2. Vistral -->
<script src="https://unpkg.com/@timeplus/vistral/dist/index.umd.min.js"></script>

<!-- 3. Usage -->
<script>
  const { Vistral, React, ReactDOM } = window;
  const { StreamChart } = Vistral;

  const data = {
    columns: [{ name: 'val', type: 'number' }],
    data: [{ val: 10 }, { val: 20 }]
  };

  const config = { chartType: 'table' };

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(React.createElement(StreamChart, { data, config }));
</script>

Peer Dependencies

Make sure you have React installed (if using NPM):

npm install react react-dom

Quick Start

import { StreamChart } from '@timeplus/vistral';

function App() {
  const data = {
    columns: [
      { name: 'timestamp', type: 'datetime64' },
      { name: 'value', type: 'float64' },
      { name: 'category', type: 'string' },
    ],
    data: [
      ['2024-01-01T10:00:00Z', 42.5, 'A'],
      ['2024-01-01T10:01:00Z', 45.2, 'A'],
      ['2024-01-01T10:02:00Z', 38.1, 'B'],
      // ... more data
    ],
  };

  const config = {
    chartType: 'line',
    xAxis: 'timestamp',
    yAxis: 'value',
    color: 'category',
    legend: true,
    gridlines: true,
  };

  return (
    <div style={{ width: '100%', height: '400px' }}>
      <StreamChart config={config} data={data} theme="dark" />
    </div>
  );
}

Chart Types

Line Chart

Line

Perfect for time series data showing trends over time.

import { StreamChart } from '@timeplus/vistral';

<StreamChart
  config={{
    chartType: 'line',
    xAxis: 'timestamp',
    yAxis: 'value',
    color: 'series',
    lineStyle: 'curve', // or 'straight'
    points: true,
    legend: true,
    gridlines: true,
    temporal: {
      mode: 'axis',
      field: 'timestamp',
      range: 5, // Show last 5 minutes
    },
    fractionDigits: 2,
  }}
  data={data}
  theme="dark"
/>

Area Chart

Area

Similar to line charts but with filled areas, great for showing volume or stacked data.

<StreamChart
  config={{
    chartType: 'area',
    xAxis: 'timestamp',
    yAxis: 'value',
    color: 'category', // Creates stacked areas
    legend: true,
  }}
  data={data}
/>

Bar Chart (Horizontal)

Bar

Horizontal bar charts for categorical comparisons.

<StreamChart
  config={{
    chartType: 'bar',
    xAxis: 'category',
    yAxis: 'value',
    color: 'subcategory',
    groupType: 'stack', // or 'dodge'
    dataLabel: true,
  }}
  data={data}
/>

Column Chart (Vertical)

Cloumn

Vertical column charts for categorical data.

<StreamChart
  config={{
    chartType: 'column',
    xAxis: 'month',
    yAxis: 'sales',
    color: 'region',
    groupType: 'dodge',
    gridlines: true,
  }}
  data={data}
/>

Single Value

SingleValue

Display a single metric with optional sparkline and delta indicator.

<StreamChart
  config={{
    chartType: 'singleValue',
    yAxis: 'activeUsers',
    fontSize: 72,
    color: 'green',
    fractionDigits: 0,
    sparkline: true,
    delta: true,
    unit: { position: 'left', value: '$' },
  }}
  data={data}
/>

Multiple Value

MultipleValue

Display multiple values side-by-side, split by a specific key. Key bound is required.

<StreamChart
  config={{
    chartType: 'multipleValue',
    yAxis: 'cpu_usage',
    key: 'server_id',
    fontSize: 48,
    color: 'cyan',
    fractionDigits: 0,
    sparkline: true,
    delta: true,
    unit: { position: 'right', value: '%' },
  }}
  data={data}
/>

Data Table

DataTable

Display streaming data in a tabular format with rich per-column styling: trend indicators, mini charts (sparkline or bar), and conditional cell/row highlighting.

<StreamChart
  config={{
    chartType: 'table',
    tableStyles: {
      timestamp: { name: 'Time', width: 200 },
      price: {
        name: 'Price',
        fractionDigits: 2,
        trend: true,                    // ▲▼ indicator; invisible placeholder preserves layout when no change
        increaseColor: '#22c55e',
        decreaseColor: '#ef4444',
      },
      volume: {
        name: 'Volume',
        miniChart: 'bar',               // Left-aligned bar for cross-row comparison
      },
      status: {
        name: 'Status',
        color: {
          type: 'condition',
          conditions: [
            { operator: 'eq', value: 'critical', color: '#EF4444', highlightRow: true },
            { operator: 'eq', value: 'warning',  color: '#F59E0B' },
          ],
        },
      },
    },
    temporal: { mode: 'key', field: 'symbol' },
  }}
  data={data}
/>

Markdown

Render live Markdown templates populated with values from streaming data. Supports all three temporal modes.

<StreamChart
  config={{
    chartType: 'markdown',
    temporal: { mode: 'key', field: 'city' },
    content: `
## Live Weather

| City     | Temp        | Humidity        |
|----------|-------------|-----------------|
| New York | {{@New York::temp_c}} °C | {{@New York::humidity}}% |
| London   | {{@London::temp_c}} °C  | {{@London::humidity}}%  |
    `,
  }}
  data={data}
/>

Use {{fieldName}} to insert the value from the latest row, or {{@keyValue::fieldName}} to look up a specific key entity (key-bound mode).

Geo Chart

GeoChart

Display geographic data points on an interactive map with pan and zoom. Two rendering engines are available via mapEngine:

  • 'l7' (default) — AntV L7 + MapLibre GL, WebGL-accelerated. Smooth pan/zoom, GPU-rendered points, free CartoDB/OSM tiles — no API token required.
  • 'canvas' — built-in canvas renderer, no extra dependencies.
<StreamChart
  config={{
    chartType: 'geo',
    mapEngine: 'l7',        // default — WebGL-accelerated via AntV L7
    latitude: 'lat',
    longitude: 'lng',
    color: 'category',      // Color points by category
    size: {
      key: 'value',         // Size points by numeric field
      min: 4,
      max: 20,
    },
    autoFit: true,          // Fit map to data bounds on each update
    showZoomControl: true,
    showCenterDisplay: true,
    pointOpacity: 0.8,
    temporal: {
      mode: 'key',
      field: 'vehicle_id',  // Track latest position per entity
    },
  }}
  data={data}
/>

Temporal Binding Modes

Vistral provides three temporal binding modes for handling streaming data:

Mode Description Use Case
axis Time mapped to axis with sliding window Time-series trends
frame Only latest timestamp visible Real-time snapshots
key Latest value per unique key Live dashboards

Axis-Bound (Sliding Window)

For time series charts, shows a sliding time window:

<StreamChart
  config={{
    chartType: 'line',
    xAxis: 'timestamp',
    yAxis: 'value',
    temporal: {
      mode: 'axis',
      field: 'timestamp',
      range: 5, // 5-minute window
    },
  }}
  data={data}
/>

Frame-Bound (Latest Timestamp)

Shows only rows with the latest timestamp - useful for real-time snapshots:

<StreamChart
  config={{
    chartType: 'table',
    temporal: {
      mode: 'frame',
      field: 'timestamp',
    },
  }}
  data={data}
/>

Key-Bound (Deduplicate by Key)

Keeps the latest value for each unique key. Supports composite keys by passing an array of fields:

<StreamChart
  config={{
    chartType: 'geo',
    latitude: 'lat',
    longitude: 'lng',
    temporal: {
      mode: 'key',
      field: ['region', 'vehicle_id'], // Composite key
    },
  }}
  data={data}
/>

Using Individual Chart Components

For complex use cases not covered by StreamChart, you can use the lower-level VistralChart with a raw grammar specification, or the specialized standalone chart components.

import { 
  VistralChart,
  SingleValueChart, 
  DataTable,
  MarkdownChart,
  GeoChart,
} from '@timeplus/vistral';

// Advanced: Use Grammar API directly for full control
<VistralChart spec={mySpec} source={data} />

// Standalone components (same as StreamChart routing, but direct)
<SingleValueChart config={config} data={data} theme="dark" />
<DataTable config={config} data={data} theme="dark" />
<MarkdownChart config={config} data={data} theme="dark" />
<GeoChart config={config} data={data} theme="dark" />

Data Format

StreamDataSource

interface StreamDataSource {
  columns: ColumnDefinition[];
  data: DataRow[];
  isStreaming?: boolean;
}

interface ColumnDefinition {
  name: string;
  type: string; // 'string' | 'number' | 'datetime64' | 'float64' | etc.
  nullable?: boolean;
}

// Data rows can be arrays or objects
type DataRow = unknown[] | Record<string, unknown>;

Example with Array Format

const data = {
  columns: [
    { name: 'time', type: 'datetime64' },
    { name: 'cpu', type: 'float64' },
    { name: 'memory', type: 'float64' },
  ],
  data: [
    [1704067200000, 45.2, 62.1],
    [1704067260000, 48.1, 63.5],
    [1704067320000, 42.8, 61.2],
  ],
};

Example with Object Format

const data = {
  columns: [
    { name: 'time', type: 'datetime64' },
    { name: 'cpu', type: 'float64' },
  ],
  data: [
    { time: '2024-01-01T10:00:00Z', cpu: 45.2 },
    { time: '2024-01-01T10:01:00Z', cpu: 48.1 },
  ],
};

Streaming Data with Hooks

Use the provided hooks for managing streaming data:

import { StreamChart, useStreamingData } from '@timeplus/vistral';

function LiveChart() {
  const { data, append, clear } = useStreamingData([], 1000); // Max 1000 items

  useEffect(() => {
    const ws = new WebSocket('ws://your-streaming-endpoint');
    
    ws.onmessage = (event) => {
      const newData = JSON.parse(event.data);
      append(newData);
    };

    return () => ws.close();
  }, [append]);

  return (
    <StreamChart
      config={config}
      data={{
        columns: [...],
        data: data,
        isStreaming: true,
      }}
    />
  );
}

Color Palettes

Built-in Palettes

import { 
  multiColorPalettes, 
  singleColorPalettes,
  findPaletteByLabel 
} from '@timeplus/vistral';

// Multi-color palettes for categorical data
// Available: 'Timeplus', 'Dawn', 'Morning', 'Midnight', 'Ocean', 'Sunset'

// Single-color palettes for sequential data
// Available: 'red', 'pink', 'purple', 'blue', 'green', 'orange', 'yellow', 'cyan', 'gray'

// Use by label
const palette = findPaletteByLabel('Dawn');

// Apply to chart
<StreamChart
  config={{
    chartType: 'line',
    xAxis: 'time',
    yAxis: 'value',
    colors: palette.values,
  }}
  data={data}
/>

Custom Colors

<StreamChart
  config={{
    chartType: 'line',
    xAxis: 'time',
    yAxis: 'value',
    colors: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'],
  }}
  data={data}
/>

API Reference

For detailed API documentation including configuration options for all chart types, hooks, and utilities, see the API Reference.

Development

Prerequisites

  • Node.js >= 16
  • npm, yarn, or pnpm

Setup

# Clone the repository
git clone https://github.com/timeplus-io/vistral.git
cd vistral

# Install dependencies
npm install

Scripts

Command Description
npm run build Build the library (CommonJS + ESM + TypeScript declarations)
npm run dev Start development mode with watch (library rebuild)
npm run dev:examples Start Vite dev server to view examples at http://localhost:3000
npm run test Run tests with Vitest
npm run test:coverage Run tests with coverage report
npm run lint Run ESLint
npm run typecheck Run TypeScript type checking

Viewing Examples

To view the interactive examples during development:

# Install dependencies first
npm install

# Start the examples dev server
npm run dev:examples

This will open http://localhost:3000 with a sidebar navigation showing all available chart examples.

Credits

Built with:

Developed by Timeplus

About

Visual + Stream , a live stream data visualization lib, follows the Grammar of Graphics

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors