Mobile Widgets
Mobile widgets are React Native components built with Re.Pack and bundled into the plugin zip. The Hiclaro mobile app loads them at runtime using Re.Pack's ScriptManager.
Build setup
Mobile widget bundles are built with Re.Pack (rspack under the hood). The output must be a single bundle.js file with no dynamic imports or lazy chunks — the file runs immediately when ScriptManager.loadScript fetches it, which is what triggers registerMobileWidgets.
The four shared packages below are provided by the host app at runtime via globalThis.__hiclaro_shared__. Declare them as externals so they are not bundled into the output — this keeps the bundle small and ensures a single React instance is shared between the host and all widgets.
Do not use Module Federation for the remote. MF requires the host to call container.init and container.get to initialise the shared scope and retrieve the exposed module. The host uses ScriptManager.loadScript and reads global.__hiclaro_widgetManifests directly — those MF methods are never called, so the entry code would never run.
Registration
When the bundle file is executed, it must call registerMobileWidgets(pluginKey, manifests) at module top level. The host reads global.__hiclaro_widgetManifests[pluginKey] immediately after the script loads to obtain the widget list. Calling registerMobileWidgets inside a component, effect, or callback means it runs too late and the widgets will not be registered.
Copy registerMobileWidgets.ts into your plugin's source directory. Both the helper and the call site look like this:
Component interface
The host passes the following props to every mobile widget component. Copy the types.ts shim into your plugin source for type-checking.
api: WidgetApi— the WidgetApi instance for this widget. It is a stable object: its identity does not change between renders. See the section below for a note on datasource dependencies.title?: string— optional display title from the widget instance configuration set by the user in the dashboard editor.Additional keys from
instanceConfigare spread onto the props object as[key: string]: unknown. Datasource and action input fields defined inplugin.json widgets[].inputsarrive this way.
WidgetApi on mobile
The mobile WidgetApi exposes the following methods. It is a subset of the full WidgetApi documented on the WidgetApi reference page — action lookup helpers and per-device triggering are not available on mobile.
getDatasourceById(id)— look up a datasource by UUID.getDatasourceByKey(key)— look up a datasource by dotted key (e.g.ariston.water_heater.current_temperature). Useful when the key is statically known and no ID input is needed.subscribeTelemetry(options)— subscribe to live telemetry. Returns an unsubscribe function; call it in theuseEffectcleanup.triggerActionByKey(key, options?)— dispatch a plugin action by its manifest key. Returns aPromise.
Typography
The host app overrides Text in the shared React Native namespace before any plugin bundle is loaded. When your plugin imports Text from react-native, it receives a wrapped version that automatically applies the app's Montserrat font family — no extra setup required.
fontWeight values are mapped to the correct Montserrat variant: '400' / 'normal' → Regular, '700' / 'bold' → Bold, and so on up to '900' → Black. Pass fontWeight in your styles as you normally would — it will be translated automatically and removed from the final style object so it does not conflict with the injected fontFamily.
Datasource subscription
The fix is to call getDatasourceByKey (or getDatasourceById) in the render body — not inside the effect — and include the resolved datasource ID as an effect dependency. When the datasource registry finishes loading, getDatasourceByKey returns a value, causing the component to re-render, the ID changes from undefined to a real UUID, and the effect re-runs and subscribes correctly.
This pattern also means you must make sure the widget is not mounted until datasources are loaded. The host gates widget rendering on the datasource context being ready — plugin widgets do not need to handle the pre-load case themselves.
Zip layout
Place the compiled bundle at widgets/mobile/bundle.js inside the plugin zip. The API detects this path automatically and sets mobileBundleUrl on the plugin response — no additional flag in plugin.json is required.
Full example
A minimal sensor widget showing the correct datasource subscription pattern. The registerMobileWidgets.ts and types.ts files above are also required in your plugin source.
Layout behaviour
The mobile dashboard renders widget instances in a vertical scroll view, ordered by grid position (y ascending, x as tiebreak). Each widget is rendered full-width — the grid w and h values from the web dashboard are ignored on mobile. Widgets size themselves vertically based on their content.
The mobile app does not support dashboard editing. Users manage the dashboard layout from the web client; the mobile app reflects it automatically.