Historian
The full historical trend chart. It loads the time range you configure and then keeps updating as new readings arrive, so one chart answers both "what happened last Tuesday" and "what is happening now".


Reach for it when you need:
- The main trend on an analysis or engineering screen
- Comparing several tags over a selectable time range
Component name: Historian · Category: Chart
In the palette: Time-series chart driven by the historian. Backfills the configured window, then tails live ticks as they arrive.
Properties
Any property can be bound to a tag, another component's property, or a query.
| Property | Type | Default | Description |
|---|---|---|---|
series | array | [] | Time-series to render. Each entry is one line, plotted against the configured time window. |
↳ series.tagPath | string | "" | Tag to plot, as its namespace PATH (e.g. /Plant/Line1/Temp) or its raw tag id. Pick from the Tags ▾ popover, type a path directly, or bind this field to a script/database column that yields a path — it is resolved to the tag id at runtime. Must have History enabled in the Namespace panel; all series in one chart should target the same storage connection. (Renamed from tagId; widgets saved earlier are still read via the legacy tagId field.) |
↳ series.label | string | "" | Optional display label. Falls back to the tag's name. |
↳ series.color | color | #2563eb | Stroke color. Defaults cycle through the palette when empty. |
↳ series.fillOpacity | number | 0 | Area fill below the line (0 = line only, 1 = solid). Subtle 0.05–0.15 reads best. Only applied when chartType='area'. |
↳ series.chartType | enum | line | Path style for this series. line = smooth interpolation between samples (default for continuous signals). area = same as line plus fill below using fillOpacity (useful for portion-of-total reads). step = right-aligned step function (correct for boolean/discrete signals where the value holds until the next sample — drawing a slope between two steady values would lie). Options: line · area · step. |
↳ series.visible | boolean | true | When false, the series line is hidden on the chart. Toggled by the Show checkbox in the tag table. Default true. |
↳ series.showPoints | boolean | false | Draw a dot at each data sample on this series (uPlot points). Off by default — dense time-series render as lines only. Enable for sparse / low-density signals where individual samples matter; on dense data the dots merge into the line. Toggled here or in the per-series pen popover. |
↳ series.yAxisPosition | enum | auto | Which SIDE this series' own y-axis is drawn on. Every series gets its OWN independently auto-scaled axis (uPlot multi-axis), stacked on the chosen side — so a 0/1 boolean and an analog reading in the thousands are each readable on their own axis. auto = derive the side from the signal type (boolean/step → right, else left), axis stays neutral grey. left / right = pin the side explicitly; the axis then takes this series' color. Options: auto · left · right. |
↳ series.compareEnabled | boolean | false | Range-mode comparison overlay. When on AND this is the ONLY visible series in Range mode, the same tag from the absolute prior window [compareStartMs, compareEndMs] is overlaid as a dashed line, time-normalized onto the current window so the two shapes can be compared. Has no effect in Live mode or when 0 / 2+ series are visible. |
↳ series.compareStartMs | number | 0 | Comparison window start, as a Unix epoch in MILLISECONDS. Easiest set via the date picker in the per-series pen popover. |
↳ series.compareEndMs | number | 0 | Comparison window end, as a Unix epoch in MILLISECONDS. Easiest set via the date picker in the per-series pen popover. |
↳ series.compareColor | color | "" | Stroke color for the comparison overlay line. Falls back to the series color when empty. |
↳ series.compareAxisPosition | enum | auto | Which y-axis the range-comparison overlay rides, mirroring the per-series Y-axis control. auto = JOIN the main y-axis (shared scale) — a same-tag period-over-period overlay usually shares magnitude, so the two shapes read directly against each other. left / right = give the comparison its OWN auto-scaled axis on that side (for when the periods differ enough that a shared scale would flatten one). Options: auto · left · right. |
timeWindowSec | number | 300 | Width of the visible time window (in seconds) in LIVE mode. Drives the initial backfill range = [now - timeWindowSec, now] AND the sliding x-axis extent. Typical presets: 60 (1m), 300 (5m), 900 (15m), 3600 (1h), 21600 (6h), 86400 (1d). Capped at 24h (86400s) — a live trend watches the moving present; for longer spans use Range mode. Ignored in Range mode (the user-picked [start, end] takes over). |
aggregate | enum | avg | How the server aggregates raw historian rows into the SAMPLE BY buckets returned to the chart. avg = single mean line per tag. minmax = avg line + area band between min/max — useful for seeing volatility around the trend (only the backfill region carries the envelope; the live edge collapses to a line). Options: avg · minmax. |
maxPoints | number | 1000 | Target number of buckets in the backfill. The server picks the SAMPLE BY bucket size as windowSec / maxPoints (floored at 1s), and hard-caps the result at 10000 rows. Higher = finer detail at cost of bigger payload; 1000–2000 fits a typical viewport at any density. Lower = faster but blockier visualization. |
autoScale | boolean | true | When true, y-axis fits data each update (with hysteresis to avoid sub-pixel jitter). When false, the axis is pinned to whatever range it last settled at — useful for setpoint-driven views where you want a fixed scale across multiple charts. |
xAxis | object | {"show":true,"showLine":true,"lineColor":"#64748b","showGrid… (truncated) | X (time) axis customization. Toggle visibility, show/hide the baseline, gridlines and ticks independently, and set the tick-label font size. |
↳ xAxis.show | boolean | true | Show this axis. When false, the axis (tick labels, ticks, baseline) is hidden but its plot-area gutter is preserved so the chart geometry stays stable. |
↳ xAxis.showLine | boolean | true | Draw the axis baseline (vertical for Y, horizontal for X). Independent of ticks and grid. |
↳ xAxis.lineColor | color | #64748b | Color of the axis baseline. Any CSS color (#hex / hsl / rgba). |
↳ xAxis.showGrid | boolean | true | Draw gridlines across the plot area at each tick position. |
↳ xAxis.showTicks | boolean | true | Draw tick marks at the axis edge. Independent of axis baseline visibility. |
↳ xAxis.tickColor | color | #64748b | Color of tick marks and tick labels for this axis. |
↳ xAxis.fontSize | number | 0 | Tick-label font size in px. 0 = the axis default (time axis 10px, value axis 14px). The axis gutter grows with it so labels never clip. 1.0.8 |
yAxis | object | {"show":true,"showLine":true,"lineColor":"#64748b","showGrid… (truncated) | Y (value) axis customization — applies to every value axis. Toggle visibility, baseline, gridlines and ticks independently, and set the tick-label font size. |
↳ yAxis.show | boolean | true | Show this axis. When false, the axis (tick labels, ticks, baseline) is hidden but its plot-area gutter is preserved so the chart geometry stays stable. |
↳ yAxis.showLine | boolean | true | Draw the axis baseline (vertical for Y, horizontal for X). Independent of ticks and grid. |
↳ yAxis.lineColor | color | #64748b | Color of the axis baseline. Any CSS color (#hex / hsl / rgba). |
↳ yAxis.showGrid | boolean | true | Draw gridlines across the plot area at each tick position. |
↳ yAxis.showTicks | boolean | true | Draw tick marks at the axis edge. Independent of axis baseline visibility. |
↳ yAxis.tickColor | color | #64748b | Color of tick marks and tick labels for this axis. |
↳ yAxis.fontSize | number | 0 | Tick-label font size in px. 0 = the axis default (time axis 10px, value axis 14px). The axis gutter grows with it so labels never clip. 1.0.8 |
timeWindowPresets | array | [60,300,900,3600,21600,86400] | List of preset windows offered in the Live-mode time dropdown, in seconds. Each value renders as one menu option (e.g. 60 → '1m', 3600 → '1h'). Defaults: [60, 300, 900, 3600, 21600, 86400] = 1m, 5m, 15m, 1h, 6h, 24h. Values above 24h (86400s) are dropped — Live mode is capped at a day; use Range mode for longer spans. |
exports | object | {"png":true,"csv":true} | Toolbar Export dropdown — toggle which formats are exposed. Both default to true. Setting either to false hides that format from the menu (not just disabled). Hide both to suppress the Export button entirely. |
↳ exports.png | boolean | true | Allow PNG image export of the chart canvas (composited with header + legend strip). |
↳ exports.csv | boolean | true | Allow CSV download of the visible buffer (ISO timestamps, one column per visible series). |
settings | object | {"timeFormat":"24h","dateFormat":"dd/MM/yy"} | Display preferences. timeFormat affects axis tick labels, the crosshair tooltip, and the PNG export's subtitle. CSV export is always ISO 8601 regardless (locale-independent for downstream tools). |
↳ settings.timeFormat | enum | 24h | Hour format used in axis labels and tooltips. '24h' = 13:30; '12h' = 1:30 PM. Options: 24h · 12h. |
↳ settings.dateFormat | string | dd/MM/yy | Date-only format for the x-axis date stamps and the crosshair tooltip's date. Tokens: dd/d (day), MMM/MM/M (month — MMM = Mar), yyyy/yy (year); other characters are literal separators. Default 'dd/MM/yy' → 31/01/26 ('dd MMM yy' → 31 Mar 26). Independent of timeFormat — never changes how the TIME is rendered. |
↳ settings.gaps | enum | auto | How NULL/missing buckets are drawn. auto (default) = the solid line BREAKS at a genuine gap and a faint DASHED bridge spans it, so a hole reads as 'missing data' (not a misleading straight line). show = always break the line at a null (a hard visible gap, no dashed bridge) for every series. connect = always bridge across nulls with a solid line (one continuous trace, no gaps). Options: auto · show · connect. |
↳ settings.alarmLimits | enum | dashed | Each series' alarm limits (above / below setpoints from its alarm configuration), drawn on its own scale in the alarm priority colour. dashed (default) | solid | off (hidden). Options: dashed · solid · off. 1.0.8 |
↳ settings.alarmEvents | boolean | true | Alarm event markers along the top of the plot: a filled triangle when an alarm was raised, a hollow one when it returned or cleared, in the priority colour. Hover one for the alarm, tag and time. Default on. 1.0.8 |
↳ settings.annotations | boolean | true | Annotations: notes operators and engineers write on the history of the chart's tags — a moment (a dashed line with a flag at the foot of the plot) or a period (a shaded band). Hover one to read it; the Annotations button lists the window's notes and lets the viewer add one. Signed-in viewers only: an anonymous visitor of a public trend sees neither. Default on. 1.0.8 |
↳ settings.bucketRange | boolean | false | Crosshair tooltip: after each value, the lowest and highest value recorded in the time slot that point covers, as (min – max) — so a spike inside a slot can be read, not just seen. Default off (the value alone). 1.0.8 |
↳ settings.integralUnit | enum | h | The time unit the Total (integral) column treats each value as a rate PER: a flow in m³/h totals to m³ with h, a power in kW to kWh with h, a count per minute with min. Default h. Options: s · min · h · d. 1.0.8 |
persistConfig | boolean | true | Remember this chart's view — selected tags, time range, live window, and display settings — in THIS browser so it survives a page reload without saving the project. Uncheck to always start from the saved config; unchecking also removes whatever this chart already stored in the browser. |
aggregations | object | {"avg":true,"min":true,"max":true,"count":true,"sum":false,"… (truncated) | Which aggregates appear as columns in the stats table when a date RANGE is selected. avg / min / max / count are on by default; enable sum, stdDev (sample) / stdDevPop (population) standard deviation, variance, range, first/last, the time-weighted average (twa) or the share of time Good (percentGood) as needed — and for production and utilities the total of a rate (integral, in settings.integralUnit), a counter's increase (counterIncrease, rollover detected), the time on / off (onSec / offSec) and the number of starts (onCount). Computed server-side over raw data in QuestDB. |
↳ aggregations.avg | boolean | true | Arithmetic mean over the range |
↳ aggregations.min | boolean | true | Minimum value |
↳ aggregations.max | boolean | true | Maximum value |
↳ aggregations.count | boolean | true | Number of samples |
↳ aggregations.sum | boolean | false | Sum of values |
↳ aggregations.stdDev | boolean | false | Sample standard deviation (QuestDB stddev_samp, ÷ n−1) |
↳ aggregations.stdDevPop | boolean | false | Population standard deviation (QuestDB stddev_pop, ÷ n) |
↳ aggregations.variance | boolean | false | Sample variance (QuestDB var_samp) |
↳ aggregations.range | boolean | false | Max − Min |
↳ aggregations.first | boolean | false | First value in the range (by timestamp) |
↳ aggregations.last | boolean | false | Last value in the range (by timestamp) |
↳ aggregations.twa | boolean | false | Average over time: each value weighted by how long it held (Bad time left out) 1.0.8 |
↳ aggregations.percentGood | boolean | false | Share of the range the tag was Good (time-weighted) 1.0.8 |
↳ aggregations.integral | boolean | false | A rate totalled over time — the area under the line (a flow in m³/h → m³ over the range). Its time unit is Settings → Total per. Bad time and gaps add nothing 1.0.8 |
↳ aggregations.counterIncrease | boolean | false | How much an ever-rising counter went up (units made, a kWh register). A rollover or reset is detected and counted 1.0.8 |
↳ aggregations.onSec | boolean | false | Time the value was not zero — a running pump, an open valve (Bad time left out) 1.0.8 |
↳ aggregations.offSec | boolean | false | Time the value was zero (Bad time left out) 1.0.8 |
↳ aggregations.onCount | boolean | false | How many times the value went from zero to not zero 1.0.8 |
cursor | object | {"lock":{"enabled":true}} | Crosshair cursor behavior. Click the chart to PIN the tooltip in place; click again to unpin. Useful for capturing exact values for screenshots or discussion. Disable if it conflicts with the drag-zoom gesture in your workflow. |
↳ cursor.lock | object | {"enabled":true} | Click-to-pin behavior for the crosshair tooltip. |
↳ cursor.lock.enabled | boolean | true | When true, clicking the plot toggles a pinned crosshair. Drag-zoom is unaffected — uPlot's drag threshold (10px) prevents accidental pins during a zoom drag. |
navigator | object | {"enabled":true,"height":48} | Range-mode zoom navigator: a small overview strip under the chart with a draggable window + grips. Drag to zoom the main chart into a sub-window (it re-queries finer data); double-click the chart to reset. Only shown in Range mode. Defaults on. |
↳ navigator.enabled | boolean | true | Show the navigator strip in Range mode. Default true. |
↳ navigator.height | number | 48 | Height of the navigator strip in pixels. 40–64 reads well; default 48. |
controls | object | {"visibility":"always","animationMs":200} | When the toolbar and the series table (plus the Range-mode navigator) are shown. always = always visible (default). hover = chart only; while the pointer is on the widget the controls slide back in and the chart shrinks to make room (a tap reveals them for a few seconds on touch screens). toggle = chart only; a button in the top-right corner slides the controls back in, and again to hide them. Hiding slides the toolbar up and the table down. 1.0.8 |
↳ controls.visibility | enum | always | always = toolbar + table always visible. hover = shown while the pointer is over the chart. toggle = hidden until the show-controls button is clicked. Options: always · hover · toggle. 1.0.8 |
↳ controls.animationMs | number | 200 | Show/hide slide duration in milliseconds (0 = no animation, max 2000). Default 200. 1.0.8 |
style | style | {} | Custom CSS properties |
Events
Attach event actions to these in the Event Manager.
| Event | Label | Group | Payload |
|---|---|---|---|
click | Click | Mouse | The DOM event. |
dblclick | Double click | Mouse | The DOM event. |
contextmenu | Right click | Mouse | The DOM event. |
mouseenter | Mouse enter | Mouse | The DOM event. |
mouseleave | Mouse leave | Mouse | The DOM event. |
contextmenu fires your configured actions at runtime only — in LiveView and in preview. On the Designer canvas the right-click is captured to open the Event Manager, so testing it there will not run your actions. Test right-click in preview.
Notes
Requires history enabled on the tags it plots and a configured historian. If the chart is empty, check those two things before anything else.
Bound the point count. A day of one-second data is 86,400 points per tag; the server aggregates it down to what the chart can draw — never more points than the chart has pixel columns.
What changed in 1.0.8
Quality is drawn, not hidden. New in 1.0.8 Stretches where a tag was Bad or Uncertain are shaded across the plot, and the crosshair tooltip names the quality and the device's reason under the series (Bad · Connection down). Bad readings are never drawn as values: the line breaks and the stretch is shaded. Uncertain values are drawn and shaded. A platform shutdown or a dead collector appears as a shaded Shutdown or Collector down stretch instead of a flat held line. In Range mode the series table gains a Quality column (Good, or Bad 12m · Uncertain 3m). The live edge breaks on a Bad reading the same way the history does, so the trend no longer rewrites itself on the next reload. → Historian overview
Spikes survive, and the line is not stair-stepped. Changed in 1.0.8 Live mode draws every series as M4: one line through each time slot's first, lowest, highest and last value, so a spike that lives in one sample stays visible at every zoom level. In Range mode Settings → Min / Max band (chart) chooses Peaks kept (min / max line), Average (single line) or Min / Max (with band). Each slot's stroke is placed at its samples' own times, not at the slot's start, so a smooth signal no longer looks like stairs. A gap shorter than a few of the tag's logging intervals is drawn solid, because that is the tag's cadence; only a real hole breaks the line with a dashed bridge. An on-change tag's value is carried across empty slots (up to 8 hours, never across a Bad stretch), and the window opens on the last stored value before it, if that value is recent enough. Integers draw linear by default; a tag's Interpolation or Settings → Historian can make them step. → Querying history
Zoom and pan. New in 1.0.8 In Range mode, Ctrl + wheel (⌘ + wheel on a Mac, or a trackpad pinch) zooms about the pointer, and Shift + drag pans. A plain drag is still the box zoom, and a plain wheel is left alone so the chart never traps a dashboard's scroll. Zoom and pan stay inside the loaded range.
Alarms on the trend. New in 1.0.8 Each series' alarm limits (from its alarm
configuration: above → Hi, or HiHi when critical; below → Lo or LoLo) are drawn on that series' own
scale, in the alarm priority colours every alarm view shares. Alarm events in the window are marked
along the top: a filled triangle when raised, a hollow one when it returned or cleared, at most 500
per window. Hovering a marker adds an Alarms section to the tooltip with the alarm, the tag,
what happened, the priority and the time. Settings → Alarms sets limit lines to dashed (the
default), solid or hidden, and turns the events on or off (settings.alarmLimits,
settings.alarmEvents); what is off is not fetched. With many pens, hover a row in the series
table to focus that pen and fade every other pen's limits and events, and click its name to pin
the focus. Each pen's popover has Alarm limits: On / Off. A viewer without alarm access sees
the trend without them.
Annotations. New in 1.0.8 Signed-in viewers can write notes on the history of the
chart's tags: a moment or a period. Annotations in the toolbar lists the window's notes. Add
switches the plot to placing mode, where a click marks a moment and a drag marks a period;
Ctrl / ⌘ + click or drag does the same without opening the panel. A note sits on top of the
line as a violet badge over a dashed guide (a moment) or a tinted band (a period); click it to open
it. Every Historian chart showing those tags draws it. An anonymous visitor of a public trend sees
no annotations. Settings → Annotations → Show annotations (settings.annotations) hides them.
More statistics. New in 1.0.8 Settings → Aggregations (range) adds
Time-weighted average, % Good, Total (integral), Counter increase, Time on,
Time off and Starts. Settings → Total per (shown while Total is on;
settings.integralUnit, per hour by default) sets the time unit a value is a rate per: m³/h totals
to m³. Over ranges longer than 7 days, time-weighted values are computed from the hourly rollup and
marked ~. → Querying history
Tooltip. Changed in 1.0.8 The crosshair shows each series' value alone. The
slot's (min – max) after it is now Settings → Tooltip → Show min – max of each point, off by
default (settings.bucketRange). Tooltip rows name tags by their name, not their id.
Export every stored sample. New in 1.0.8 Export → CSV — every stored sample downloads every stored row of the visible window at full resolution, built by the server, in the project time zone, with quality, status and origin, up to 1,000,000 rows. CSV — time series is still the points the chart draws, and now writes timestamps in the project time zone with their offset instead of UTC.
Controls and layout. New in 1.0.8 controls.visibility decides when the toolbar and
series table show: Always (the default), On hover (they slide in while the pointer is on the
widget, and a tap reveals them on a touch screen), or Toggle button (a button beside the title).
controls.animationMs sets the slide (200 ms by default). A viewer can change it in Settings →
Controls. On a narrow widget the toolbar folds: Tags, Live and Range become icons, then
Fullscreen, Settings and Export fold into one More menu, instead of wrapping over the chart.
The axis tick-label size (xAxis.fontSize / yAxis.fontSize) widens the axis gutter with it, so
labels never clip; 0 means the axis default, which is now 10 px for time and 14 px for values.
Accessible. New in 1.0.8 To a screen reader the plot is an image named in words (Trend of Flow, Temp from … to …. 3 alarm events in the window.). Visually hidden tables list the alarm limits and the window's alarm events, and the series table is a real table with row counts, so a reader announces row 5 of 40.
Reads through the designated historian. Changed in 1.0.8 The chart reads through Settings → Historian's connection, which reaches earlier stores through the epoch log, and no longer through each tag's own storage connection. That pointer could name a historian since deleted, which left the chart empty while its data was being recorded. In LiveView this works for operators too.