SmartChart (Preview)
A fully-featured, declarative charting Web Component built on Chart.js and ApexCharts.
Fetch data from an API, a smart-data state key, or inline — then add
date-range filters, a type switcher, goal lines, threshold annotations, drag-to-zoom,
fullscreen, CSV/PNG/JSON export, and live WebSocket streaming — all through HTML attributes.
Zero JavaScript required in your templates.
<script src="smart-chart.js"></script>
<!-- Load AFTER smart-state.js and smart-data.js if using source= -->
Load Order
SmartChart loads Chart.js and the annotation plugin automatically if they aren't already on the page.
When using websocket=, it also loads ApexCharts on first message. You never need to
include these CDN URLs yourself — but loading them earlier speeds up the first render.
<!-- Optional: pre-load deps for faster first render --> <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script> <!-- Only needed for WebSocket live charts --> <script src="https://cdn.jsdelivr.net/npm/apexcharts@3/dist/apexcharts.min.js"></script> <!-- Smart state layer (needed only for source= / state-listen=) --> <script src="smart-state.js"></script> <script src="smart-data.js"></script> <!-- SmartChart — NOT type="module", load as regular script --> <script src="smart-chart.js"></script>
Data Source: API / Fetch
Set api= to a URL. SmartChart fetches with Accept: application/json
and auto-maps the response. Add refresh="30s" for polling.
When ranges= is also set, SmartChart appends ?range=7d to the URL
so your backend can handle server-side filtering too.
<!-- Simplest possible — SmartChart auto-detects x/y fields --> <smart-chart api="/api/revenue/" x-field="date" y-field="amount" default-type="area" title="Revenue" ></smart-chart> <!-- With auto-refresh every 30 seconds --> <smart-chart api="/api/revenue/" x-field="date" y-field="amount" refresh="30s" toolbar="refresh,fullscreen" title="Revenue" ></smart-chart> <!-- Nested response — use response-path for { "data": { "items": [...] } } --> <smart-chart api="/api/sales/" response-path="data.items" x-field="date" y-field="sales" ></smart-chart> # Django view — return flat JSON array directly def revenue_api(request): data = list(Sale.objects.values('date', 'amount')) return JsonResponse(data, safe=False)
Data Source: SmartData (source=)
The most powerful pattern. Declare a <smart-data> element that fetches and
caches data into a named key in smartState. Multiple charts and a
<smart-table> can all read from the same key — one fetch, many consumers.
Add state-listen= to automatically re-render when a filter state key changes.
<!-- 1. Declare the data layer — fetches once, refreshes every 5s --> <smart-data key="salesData" api="/dummy-chart-data/" refresh="5s" cache="5s" ></smart-data> <!-- 2. A filter bar that writes to state key "status" --> <smart-filter-bar target="salesTable" auto-apply state-key="salesFilters"> <smart-input name="status" type="select" data-options='[{"id":"","name":"All"},{"id":"active","name":"Active"}]' state-set="status"> </smart-input> </smart-filter-bar> <!-- 3. Chart reads from same key, re-renders when "status" state changes --> <smart-chart source="salesData" state-listen="status" x-field="date" y-field="sales" default-type="area" title="Revenue Over Time" ></smart-chart> <!-- 4. Table reads from same key — one fetch serves both --> <smart-table source="salesData" id="salesTable"></smart-table>
Data Source: Inline
Pass data directly as a JSON string via data=. Use labels= for
custom x-axis labels on flat value arrays. Good for static charts rendered from Django context.
<!-- Flat array of values --> <smart-chart data="[120, 340, 210, 480, 390, 560]" labels='["Jan","Feb","Mar","Apr","May","Jun"]' default-type="bar" title="Monthly Sales" palette="ocean" ></smart-chart> <!-- Array of objects — same shape as API response --> <smart-chart data='[{"month":"Jan","revenue":1200},{"month":"Feb","revenue":1800}]' x-field="month" y-field="revenue" default-type="line" ></smart-chart> # Django — render with template variable <smart-chart data="" x-field="date" y-field="value" ></smart-chart>
Data Source: WebSocket Live
Add websocket= to stream live data. SmartChart loads ApexCharts for the live
view — it provides native right-to-left scrolling, smooth entry animations, and a
live stats bar showing the latest value, trend delta, and velocity per tick. The WebSocket
auto-reconnects with exponential backoff and auto-pauses rendering when the browser tab is hidden.
<!-- append mode: server pushes one point at a time --> <smart-chart websocket="ws://127.0.0.1:8000/ws/chart/random/" ws-mode="append" ws-max-points="60" ws-show-status default-type="area" type-switcher="area,line,bar" palette="vivid" title="Live Sales" y-field="sales" ></smart-chart> <!-- replace mode: server pushes full dataset each time --> <smart-chart websocket="ws://127.0.0.1:8000/ws/leaderboard/" ws-mode="replace" default-type="bar" x-field="name" y-field="score" ></smart-chart> # Django Channels consumer — append mode message format async def websocket_connect(self, event): await self.accept() while True: await self.send(json.dumps({ "label": datetime.now().strftime("%H:%M:%S"), "values": { "sales": random.randint(200, 900) } })) await asyncio.sleep(1) # Or use flat format — any fields detected automatically # { "date": "14:32:07", "sales": 450, "returns": 30 }
Chart Types
Set default-type= for the initial type. All Chart.js types are supported.
Live Type Switcher
Add type-switcher= to render icon buttons in the chart header that let users
switch between chart types in real time. The chart re-renders with the current data —
no page reload, no re-fetch.
<smart-chart api="/api/revenue/" x-field="date" y-field="amount" default-type="area" type-switcher="bar,line,area" title="Revenue" ></smart-chart> // type-switcher renders an icon button per listed type in the header. // default-type is the initial chart type — it doesn't need to be in the switcher list.
Date Range Filtering
Add ranges= to render range buttons (7d, 30d, 90d, YTD, 1y, all, etc.).
SmartChart filters in memory — anchored to the newest data point, not today's date —
so your API doesn't need to support filtering. When api= is used, it also
appends ?range=7d for server-side filtering if you want it.
<smart-chart api="/api/sales/" x-field="date" y-field="sales" ranges="7d,30d,90d,ytd,1y,all" default-type="area" title="Sales" ></smart-chart> // Supported range tokens: // Nd = last N days (7d, 30d, 90d) // Nw = last N weeks (2w, 4w) // Nm = last N months (3m, 6m) // Ny = last N years (1y, 2y) // ytd = year to date // all = show all data (default when no range selected)
Goal Lines & Threshold Annotations
goal-line= renders a thick solid green line — the primary target.
thresholds= accepts a JSON object of value: color pairs
and renders thin dashed warning lines. Both appear in both the inline and fullscreen views.
<smart-chart api="/api/sales/" x-field="date" y-field="sales" goal-line="500" goal-label="Target (500)" thresholds='{"700":"orange","900":"red"}' default-type="area" title="Sales vs Target" ></smart-chart> // goal-line — thick 3px solid green line at y=500 // with "Target (500)" label on the left end // // thresholds — { "700": "orange" } → thin dashed orange line at y=700 // { "900": "red" } → thin dashed red line at y=900 // Also accepts hex: { "700": "#f97316" }
Drag-to-Zoom
Drag horizontally on any line, bar, or area chart to zoom into that x-axis range.
A blue selection rectangle appears during the drag. Double-click or press
⊖ Reset Zoom to restore the full view. Zoom is active by default —
add no-zoom to disable it. Zoom is automatically disabled on pie,
doughnut, radar, and WebSocket live charts.
<!-- Zoom is ON by default for bar/line/area/step charts --> <smart-chart api="/api/sales/" x-field="date" y-field="sales" toolbar="fullscreen" ></smart-chart> // → Drag on the plot area to zoom in // → Double-click or click "⊖ Reset zoom" to restore <!-- Disable zoom --> <smart-chart api="/api/sales/" no-zoom></smart-chart> // In fullscreen mode: a "Drag to zoom · Double-click to reset" hint toast // fades in at the bottom of the chart, then disappears after 3s.
Fullscreen Mode
Add fullscreen to toolbar=. Clicking ⛶ opens a full-viewport
overlay with the chart re-rendered at maximum size. Range buttons and the type switcher
are duplicated in the fullscreen header. Drag-to-zoom works in fullscreen too.
Press Escape or click the backdrop to close.
<smart-chart api="/api/revenue/" x-field="date" y-field="amount" toolbar="refresh,fullscreen" ranges="7d,30d,1y,all" type-switcher="bar,line,area" title="Revenue" ></smart-chart> // toolbar accepts: refresh, fullscreen (comma-separated) // export is separate: export="png,csv,json"
Export
Add export= to render export buttons in the header. PNG exports the rendered
canvas. CSV exports labels + dataset values as a spreadsheet. JSON exports the original raw
response data — exactly what came from the API.
<smart-chart api="/api/sales/" x-field="date" y-field="sales" toolbar="refresh,fullscreen" export="png,csv,json" title="Sales Data" ></smart-chart> // PNG → downloads chart as image (uses canvas.toBase64Image()) // CSV → Label, Dataset1, Dataset2 … rows for all visible data points // JSON → raw _rawData as downloaded from the API (current range/filter applied)
Palettes
Set palette= to control dataset colors. Palettes cycle when you have more
datasets than colors.
palette="material" // Google Material colors (default) palette="nord" // Nordic muted tones palette="monochrome" // Single-hue indigo shades palette="pastel" // Soft pastels — good on dark backgrounds palette="ocean" // Deep blue tones palette="vivid" // High-contrast neons
Multi-Series Charts
Use datasets= to plot multiple series from the same data. Each entry in
the array specifies a field, optional label, color,
and per-series type (useful for mixed bar + line charts).
<!-- Two series from the same API response --> <smart-chart api="/api/sales/" x-field="date" datasets='[ {"field":"sales", "label":"Sales", "type":"bar"}, {"field":"returns", "label":"Returns", "type":"line","color":"#f87171"} ]' palette="material" title="Sales vs Returns" ></smart-chart> // datasets fields: // field — key in your data row (required) // label — legend/tooltip label (defaults to field name) // color — CSS color or hex (defaults to palette color) // type — per-series chart type (bar|line|area) for mixed charts
Sync Groups
Assign multiple charts to the same sync-group= name. Hovering over a data
point on one chart highlights the same index on all other charts in the group simultaneously.
Useful for comparison dashboards.
<smart-chart api="/api/sales/" x-field="date" y-field="sales" sync-group="overview" title="Sales" ></smart-chart> <smart-chart api="/api/costs/" x-field="date" y-field="cost" sync-group="overview" title="Costs" ></smart-chart> // Hovering over "Mar 15" on the Sales chart highlights // "Mar 15" on the Costs chart simultaneously. <!-- click-state writes clicked point to smartState --> <smart-chart api="/api/sales/" click-state="selectedPoint" ></smart-chart> // After click: smartState.get("selectedPoint") // → { label: "Mar 15", value: 480, datasetIndex: 0, index: 14 }
Sales Dashboard — Full-Featured
A complete chart combining date ranges, type switcher, goal line, threshold warnings,
export, fullscreen, and auto-refresh — the pattern from helper.html.
<smart-data key="salesData" api="/dummy-chart-data/" refresh="5s" cache="5s" ></smart-data> <smart-chart default-type="area" type-switcher="bar,line,area" source="salesData" state-listen="status" x-field="date" y-field="sales" palette="pastel" goal-line="500" goal-label="Target (500)" thresholds='{"700":"orange","900":"red"}' ranges="7d,30d,90d,ytd,1y,all" toolbar="refresh,fullscreen" export="png,csv,json" title="Revenue Over Time (~Refreshes every 5s)" ></smart-chart>
Live WebSocket Dashboard
Pair a WebSocket chart with a second API-backed chart on the same page. They operate completely independently — the WebSocket chart uses ApexCharts, the static chart uses Chart.js.
<!-- Live streaming chart --> <smart-chart websocket="ws://127.0.0.1:8000/ws/chart/random/" ws-mode="append" ws-max-points="15" ws-show-status palette="pastel" default-type="area" type-switcher="area,line,bar" title="Live Sales" subtitle="connecting..." ></smart-chart> <!-- Static chart below uses source= from smart-data --> <smart-chart source="salesData" x-field="date" y-field="sales" default-type="bar" title="Historical" ></smart-chart> # Django Channels routing — routing.py from channels.routing import ProtocolTypeRouter, URLRouter from django.urls import re_path from myapp.consumers import ChartConsumer application = ProtocolTypeRouter({ "websocket": URLRouter([ re_path(r"ws/chart/random/", ChartConsumer.as_asgi()), ]), })
SmartData + Filter Bar + Table
The full pattern from helper.html — one smart-data feeds a chart
and a table. The filter bar writes to state and both consumers re-render reactively.
<smart-data key="salesData" api="/dummy-chart-data/" refresh="5s" cache="5s" ></smart-data> <smart-filter-bar target="salesTable" auto-apply state-key="salesFilters" > <smart-input name="status" label="Status" type="select" data-options='[{"id":"","name":"All"},{"id":"active","name":"Active"}]' state-set="status" ></smart-input> </smart-filter-bar> <smart-chart source="salesData" state-listen="status" x-field="date" y-field="sales" default-type="area" type-switcher="bar,line,area" ranges="7d,30d,90d,all" palette="pastel" goal-line="500" goal-label="Target" toolbar="refresh,fullscreen" export="png,csv,json" title="Revenue Over Time" ></smart-chart> <smart-table source="salesData" id="salesTable" ></smart-table>
All Attributes
| Attribute | Type | Description | Default |
|---|---|---|---|
| api | string | URL to fetch chart data from. SmartChart adds ?range= when a range is selected. | — |
| source | string | Key in smartState (set by <smart-data>). Chart re-renders when this key updates. | — |
| data | JSON | Inline data as a JSON array of numbers or objects. | — |
| labels | JSON | X-axis labels for flat data= arrays. | — |
| websocket | string | WebSocket URL for live streaming data (uses ApexCharts). Auto-reconnects with exponential backoff. | — |
| ws-mode | string | append — server pushes one point at a time. replace — server pushes full dataset. | append |
| ws-max-points | number | Max data points to keep in the rolling window before oldest are dropped. | 200 |
| ws-show-status | boolean | Show ⬤ Live / ↺ Reconnecting in the subtitle area. | false |
| x-field | string | Key in each data row to use as the x-axis label. Required for date range filtering. | auto |
| y-field | string | Key in each data row to use as the single y-axis dataset. | auto |
| datasets | JSON | Array of {field, label?, color?, type?} for multi-series or mixed charts. | — |
| response-path | string | Dot-path to extract nested data from API response, e.g. data.items. | — |
| default-type | string | Initial chart type: bar · line · area · pie · doughnut · radar · scatter · bubble · step · horizontalBar · polarArea. | bar |
| type-switcher | string | Comma-separated list of chart types to render as icon switcher buttons. | — |
| palette | string | material · nord · monochrome · pastel · ocean · vivid. | material |
| title | string | Chart card heading. | — |
| subtitle | string | Smaller text below the title. | — |
| height | number | Canvas height in pixels. | 320 |
| tension | number | Line/area curve tension 0–1. | 0.4 |
| point-radius | number | Data point dot radius. | 3 |
| goal-line | number | Y value for the goal annotation (thick solid green line). | — |
| goal-label | string | Label for the goal line pill. | Goal: {value} |
| thresholds | JSON | Object of {"value": "color"} pairs for threshold warning lines. | — |
| ranges | string | Comma-separated range tokens. Renders filter buttons. Requires x-field= to be a date. | — |
| toolbar | string | Comma-separated toolbar items: refresh · fullscreen. | — |
| export | string | Comma-separated export formats: png · csv · json. | — |
| refresh | string | Auto-poll interval for api= mode. Format: 30s · 5m · 1h. | — |
| state-listen | string | Re-render the chart when this smartState key changes. | — |
| click-state | string | Write clicked data point to this smartState key. | — |
| sync-group | string | Group name for synchronized tooltip highlighting across multiple charts. | — |
| no-zoom | boolean | Disable drag-to-zoom. Zoom is automatically disabled for pie/doughnut/radar and WebSocket charts. | false |
Events
| Event | detail | Fires when |
|---|---|---|
| smart-chart-loaded | { key } | Data has been fetched and the chart has finished rendering. |
| smart-chart-click | { label, value, datasetIndex, index } | User clicks a data point on the chart. |
| smart-chart-ws-status | { state } | WebSocket connection state changes. state is "live" · "reconnecting" · "closed". |
const chart = document.querySelector('smart-chart'); chart.addEventListener('smart-chart-click', (e) => { console.log('Clicked:', e.detail.label, e.detail.value); }); chart.addEventListener('smart-chart-ws-status', (e) => { // e.detail.state is "live" | "reconnecting" | "closed" updateStatusBadge(e.detail.state); });
Data Formats
SmartChart auto-detects the format. It handles four shapes: flat arrays, object arrays with
explicit x-field/y-field, object arrays with auto-detected numeric
fields, and pre-formatted Chart.js objects.
// A: Flat array (use labels= for x-axis) [120, 340, 210, 480] // B: Object array — explicit x/y (most common Django API response) [ { "date": "2026-01-01", "sales": 480 }, { "date": "2026-01-02", "sales": 390 } ] // C: Object array — multi-column (auto-detected) [ { "date": "2026-01-01", "sales": 480, "returns": 30 } ] // → SmartChart plots all numeric columns as separate series // D: Pre-formatted Chart.js object (passed through directly) { "labels": ["Jan","Feb"], "datasets": [...] } // WebSocket append message format: { "label": "14:32:07", "values": { "sales": 450 } } // or flat (SmartChart uses x-field= for the label key): { "date": "14:32:07", "sales": 450 }