Consigne

Web Components for SCADA and HMI dashboards

Web Components are custom HTML elements built on browser standards, so one set of HMI widgets (tanks, analog bars, alarm lists, confirmed commands) works in Angular, React or a plain HTML page. They suit supervision screens well because those screens outlive frameworks. The work that remains is yours: feed them live data, show when that data is stale, and keep them usable with a keyboard and without relying on colour.

Published 27 October 2026 · 6 min · by Antoine Despres, Consigne

Why Web Components fit HMI work

An HMI or SCADA web screen has a long life. The plant it supervises runs for decades, while front-end frameworks change major versions every year or so. A tank, a limit-aware bar or an alarm list written as a component of one framework ties the display layer to that framework's upgrade schedule. Written as a standard custom element, it is just an HTML tag that any page can use.

Three properties of Web Components matter in this context:

  • Framework-agnostic. A custom element is registered with the browser, not with a framework. Angular documents custom elements as a Web Platform feature available on all the browsers it supports, and React 19 passes all the tests of the Custom Elements Everywhere suite.
  • Encapsulated. With shadow DOM, the internal markup and styles of a component do not leak into the page and the page's CSS does not break the component. Theming goes through CSS custom properties, which do cross the shadow boundary, and through ::part() for the parts a component chooses to expose.
  • Driven by attributes, properties and events. That is the same contract as a native <input>: you set values in, you listen for events out. It maps naturally onto tags and operator actions.

The trade-off: you lose framework conveniences inside the component itself (templating, dependency injection), and each framework has small integration details to get right. Those details are covered below.

What a supervision component needs that a chart library lacks

Generic UI kits and chart libraries are built for business dashboards. A supervision screen needs different primitives:

NeedWhat the component must do
Value against its normal rangeshow LL, L, H and HH limits and switch to an abnormal state outside them
Data qualitydistinguish good, uncertain, bad and stale values, in words as well as in colour
Alarmssort by acknowledgement and priority, and emit an acknowledgement event instead of changing your data
Commandsask for explicit confirmation, time out, and leave the real state to the equipment's feedback
Setpointsreject values outside the allowed range before anything is sent
Trendsfixed scales per pen, a time axis and a read-out cursor, without rescaling on every update

The high-performance approach behind ISA-101 adds a visual rule: a neutral display in normal operation, with colour reserved for abnormal situations. The guide High-performance HMI: 8 rules for a web screen details it.

Using them in plain HTML, Angular and React

Plain HTML

A <script type="module"> that imports the library is enough. Values update when an attribute changes:

<hmi-analog-bar label="Tank level" value="62" min="0" max="100"
  ll="10" l="20" h="80" hh="90" unit="%"></hmi-analog-bar>
<script type="module">
  import './hmi-kit/dist/hmi-kit.min.js';
  document.querySelector('hmi-analog-bar').setAttribute('value', '63');
</script>

If the tags render empty, the script was most likely not loaded as a module.

Angular

Angular needs to be told that unknown elements are expected: add CUSTOM_ELEMENTS_SCHEMA to the standalone component's schemas, otherwise compilation fails with "is not a known element". Bind attributes with [attr.value], bind objects such as an alarm array as properties ([alarms]), and listen to custom events with the usual syntax, (hmi-ack)="acknowledge($event)". With strict templates, $event is typed Event: cast it to CustomEvent to read detail. Signals work well here, since the component only needs the current value.

React

React 19 assigns props as properties when the custom element defines a matching property, and as attributes otherwise. Arrays and objects, such as a list of alarms, can therefore be passed directly. React 18 treated unknown props as attributes, so objects did not reach the element: set them through a ref instead. A reliable way to receive custom events is addEventListener on that ref, in an effect that also removes the listener. With Next.js, render the components in a client component ('use client').

Feeding real-time data

The browser cannot talk Modbus or OPC UA directly. In practice a gateway on the server side reads the PLC or the OPC UA server and pushes values to the page over WebSocket, server-sent events or an MQTT broker reachable over WebSocket. The page then has to dispatch each value to the right element.

A simple, robust pattern is to tag each element with the variable it shows and keep a single dispatcher:

<hmi-analog-bar data-tag="LT-01" label="Level T-01" min="0" max="100" h="80" hh="90" unit="%"></hmi-analog-bar>

The dispatcher receives a message such as { "LT-01": 58.2 }, finds the elements whose data-tag matches, and updates them. Three rules keep it honest:

  1. Carry quality with the value. A value from a failed sensor or a simulated input must not look like a good measurement.
  2. Mark everything stale when the link drops. On a WebSocket close event, or when no message has arrived for a set time, switch the displayed values to a stale state rather than leaving the last value as if it were live.
  3. Batch high-frequency updates. Updating one attribute only redraws one component, but hundreds of values refreshed more than ten times a second are better grouped into one requestAnimationFrame callback.

Commands go the other way: the component emits an event, your application sends the request to the gateway, and the displayed state changes only when the equipment reports that it has actually started or stopped.

Accessibility on a supervision screen

Accessibility on an HMI is not only about compliance. Operators work long shifts, some are colour-blind, and control rooms have glare and distance. The useful checks are concrete:

  • Colour is never the only cue (WCAG 1.4.1). An alarm priority also needs a shape or a number; a state or a data quality also needs a word.
  • Contrast: 3:1 for badges, symbols and trend lines against adjacent colours (WCAG 1.4.11), 4.5:1 for normal text. Yellow and orange on a light grey background fail; a dark outline fixes that. The HMI colour contrast checker computes the ratios.
  • Semantics: an analog indicator can expose role="meter" with aria-valuenow, aria-valuemin and aria-valuemax, plus a text that includes the state.
  • Keyboard: acknowledging an alarm and confirming a command must work without a mouse, and that is worth an automated end-to-end test.

An off-the-shelf set: hmi-kit

hmi-kit is a set of 16 Web Components built on these rules: process graphics with animated pipes and 117 P&ID symbols, tanks and LL/L/H/HH analog bars, multi-pen trends, alarm banner and list with acknowledgement, a filterable event log with CSV export, confirmed start and stop commands, bounded setpoints, permissives and step sequences. A bindTags helper implements the data-tag pattern above, with data quality and a stale state. It has no runtime dependency (about 22 KB gzipped), ships with an Angular 21 example and React 19 tests, and its component texts switch to English with setLocale('en'), since they default to French.

Its limits: it does not connect to any PLC or protocol, it is not a complete SCADA package or a screen editor, it is not ISA-101 certified, and it is not a safety system. It is sold under a commercial licence. For the design reasoning behind such screens, the High-Performance HMI guide covers palettes, alarms, data quality and a Web Components architecture with Angular and React examples.

Sources

More guides