Developing Custom Modules#
This section explains how to develop custom modules for the BaSyx AAS Web UI.
Modules are the right extension mechanism when you want to implement larger, workflow-oriented features that go beyond a single Submodel or SubmodelElement visualization. Typical examples include import/export workflows, cross-submodel analytics, dashboards, or domain-specific tools.
What Is a Module?#
A module is a self-contained application feature that:
Has its own route under
<web-ui-url>/modules/...Appears as an entry in the Modules tab of the main menu
Runs inside the standard application shell (app bar, footer)
Has full control over its internal layout and navigation
Modules are typically used when:
Multiple submodels or submodel elements are involved
Business logic spans more than a single visualization panel
Dedicated workflows, forms, or dashboards are required
Where Modules Live#
All modules are located in:
src/pages/modules/
Simple modules#
A single Vue file placed directly in this directory automatically becomes a module:
src/pages/modules/MyModule.vue
This creates:
Route:
/modules/mymoduleMenu entry: MyModule (or a custom title)
Larger modules#
For larger modules, use a dedicated directory with an index.vue entry file (recommended):
src/pages/modules/PcfProcess/
├── index.vue
├── routes.ts ← optional nested route manifest
├── components/
├── composables/
├── stores/
└── utils/
A backward-compatible alternative keeps the entry file outside the directory:
src/pages/modules/PcfProcess.vue
src/pages/modules/PcfProcess/
├── PcfProcess.routes.ts ← optional nested route manifest
├── components/
├── composables/
├── stores/
└── utils/
Best practices:
Prefer the
index.vueconvention for new modulesThe directory name and top-level
.vuefile should match (when using the legacy layout)The top-level file acts as the module entry point
Internal views/components are imported from the module directory
Automatic Route Generation#
Module routes are generated dynamically at startup. The router scans for both top-level .vue files and index.vue files inside module directories:
const moduleFileRecords = import.meta.glob('@/pages/modules/*.vue');
For each discovered module:
The file or directory name becomes the route name and base path
The component is lazy-loaded
Route metadata is derived from
defineOptionsIf a route manifest exists (
routes.tsor.routes.ts), its child routes are registered automatically
Example generated route:
/modules/pcfprocess
Hint
No manual router configuration is required — this includes nested routes defined via a route manifest.
Module Options#
Modules declare metadata using defineOptions:
defineOptions({
inheritAttrs: false,
isDesktopModule: true,
isMobileModule: true,
isVisibleModule: true,
isOnlyVisibleWithSelectedAas: false,
isOnlyVisibleWithSelectedNode: false,
moduleTitle: 'PCF Process',
});
Option reference#
Option |
Description |
|---|---|
|
Display name in the Modules menu (defaults to file name) |
|
Visible in desktop layout (default: |
|
Visible in mobile layout (default: |
|
Controls menu visibility (route still accessible) |
|
Only visible when an AAS is selected |
|
Only visible when a Submodel or SME is selected |
|
Preserve |
Note
If isOnlyVisibleWithSelectedAas or isOnlyVisibleWithSelectedNode is set, preserveRouteQuery is enabled automatically.
Hotkeys#
Modules can also define their own hotkeys. Hotkeys are a way to provide keyboard shortcuts for actions within the module.
In order to define hotkeys for a module, it is recommended to create a second <script> block (without setup) in the module’s main Vue file. This block should import and use the type PageShortcutDefinitions from the useRouteShortcuts composable.
The following example showcases how to define a hotkey that clears an asset ID input field when the user presses Cmd+Shift+Backspace:
<script lang="ts">
import type { PageShortcutDefinitions } from '@/composables/Shortcuts/useRouteShortcuts';
// Module shortcuts definition - available when this module is active
export const shortcuts: PageShortcutDefinitions = () => [
{
id: 'my-module-clear-asset-id',
title: 'Clear Asset ID',
description: 'Clear the asset ID input field',
prependIcon: 'mdi-eraser',
category: 'My Module Shortcuts',
keys: 'cmd+shift+backspace',
handler: (event: KeyboardEvent) => {
event.preventDefault();
event.stopPropagation();
const assetIdInput = document.querySelector('#asset-id-input input') as HTMLInputElement;
if (assetIdInput) {
assetIdInput.value = '';
assetIdInput.dispatchEvent(new Event('input', { bubbles: true }));
}
},
},
];
</script>
Hint
For more details on defining hotkeys, refer to the Defining Hotkeys section.
Mobile vs Desktop Modules#
The application distinguishes between mobile and desktop layouts using responsive breakpoints.
Use isDesktopModule and isMobileModule to control where a module appears.
Typical patterns:
Complex workflows: desktop-only
Viewer-style dashboards: desktop + mobile
Accessing Context and State#
Selected AAS and Node#
Modules should access the current selection via the AAS Store:
import { useAASStore } from '@/store/AASDataStore';
const aasStore = useAASStore();
const selectedAas = aasStore.getSelectedAAS;
const selectedNode = aasStore.getSelectedNode;
If preserveRouteQuery is enabled, the following are also available via the router:
aasquery parameterpathquery parameter
This allows reloading the page without losing context.
Loading and Modifying AAS Data#
Modules should reuse the same composables as core pages and plugins:
useAASHandlinguseSMHandlingClient composables for repositories and registries
Warning
Direct API calls to AAS backend services are discouraged.
Routing Inside Modules#
Each module has a base route:
/modules/<module-name>
Nested Routes via Route Manifest#
Modules can define nested child routes through a dedicated route manifest file. This is the recommended approach for modules that require their own internal navigation (e.g. multi-step workflows, tabbed interfaces, or detail views).
Depending on the module layout, the manifest is placed as follows:
Module layout |
Entry file |
Route manifest |
|---|---|---|
Directory with |
|
|
Flat file (legacy) |
|
|
A route manifest exports a default object with a children array:
// src/pages/modules/MyModule/routes.ts
export default {
children: [
{
path: 'technical-data',
name: 'TechnicalData',
component: () => import('@/pages/modules/MyModule/TechnicalData.vue'),
},
],
};
The example above produces the route /modules/mymodule/technical-data.
Rules for nested routes#
Child paths must be relative (no leading
/) and stay inside/modules/<module-name>/**.Child route names are automatically prefixed with
<ModuleName>__to avoid collisions (e.g.MyModule__TechnicalData).Child routes inherit module visibility, platform, and query metadata by default.
Existing modules without a route manifest continue to work unchanged.
Hint
The module entry component (index.vue / MyModule.vue) must include a <router-view /> element for child routes to render.
Alternative approaches#
For simpler internal navigation that does not require distinct URLs, modules can also use:
Internal tabs or step components
Navigation drawers
Conditional rendering
Deep linking via nested routes is encouraged but not required.
Layout Guidelines#
Modules are rendered inside the standard application shell:
App bar and menus are provided by the host application
The module controls the main content area
Recommended layout pattern:
<v-container fluid>
<!-- module content -->
</v-container>
Typical module layout inside the application shell.#
Feature Flags and Security#
Modules must respect global feature flags:
ALLOW_EDITINGALLOW_UPLOADINGrelated environment flags
These flags are available via the Environment Store.
Warning
Accessing the Infrastructure Store directly from modules should be avoided unless absolutely necessary.
Some modules (e.g. import/export workflows) may intentionally interact with multiple infrastructures, but this should be done explicitly and with care.
Testing Modules#
There is currently no strict testing requirement for modules.
Recommended approach:
Move complex logic into composables or utils
Test those units as described in Testing & Quality Assurance
Use manual testing for UI-heavy workflows
Existing Modules as Reference#
The following modules demonstrate common patterns:
Test – minimal module structure
PcfProcess – dedicated module directory, AAS & SM handling
AasImporter – infrastructure access and data transfer
QueryLanguage – client composables usage
TestPreserveUrlQuery – route query preservation
Hint
Studying existing modules is often the fastest way to understand best practices.
Summary#
Modules provide a powerful extension mechanism for the BaSyx AAS Web UI:
Automatic routing and menu integration
Full-screen application features
Access to shared state, composables, and infrastructure
Clear separation from submodel-level plugins
When a feature grows beyond a single semantic visualization, a module is usually the right architectural choice.